python이란?

  • 병렬 실행 불가능 (https://it-eldorado.tistory.com/160)
    • Python 인터프리터 : Python으로 작성된 코드를 한 줄 씩 읽으면서 실행 하는 프로그램 → CPython
    • GIL ( Global Interpreter Lock) → 한 프로세스 내에서, Python 인터프리터는 한 시점에 하나의 쓰레드에 의해서만 실행 가능 ( Python 의 객체들에 대한 접근을 보호하는 일종의 뮤텍스 Mutex)
      • Mutex : 멀티 쓰레딩 환경에서 여러개의 쓰레더가 어떠한 공유 자원에 접근 가능 할 떄, 그 공유 자원에 접근하기 위해 가지고 있어야 하는 일종의 열쇠
    •  
    • GIL이 필요한 이유? Race Condition을 방지하기 위해 Mutex 필요
      • Python 에서 GC(Garbage Collection)는 Object의 참조횟수가 0이 되면 해당 객체를 메모리에서 삭제 시키는 메커니즘으로 동작
      • 여러개의 Thread 가 Python 인터프리터를 동시에 실행하면 Race Condition(하나의 값에 여러 쓰레드가 동시에 접근해서 값이 올바르지 않게 Read/Write 할 수 있는 상태)이 발생 할 수도 있음
    • → CPU연산이 비중이 적은, 즉 외부 연산(I/O, Sleep 등) 의 비중이 큰 작업을 할 때는 멀티 쓰레딩이 굉장히 좋은 성능!

 

LangFlow

 

 

FastAPI

 

 

 

LangChain

  • LLM 프롬프트의 실행과 외부 소스의 실행(계산기, 구글 검색, 슬랙 메시지 전송이나 소스코드 실행 등)을 엮어 연쇄(Chaining)하는 것
  • https://python.langchain.com/docs/get_started/introduction
  • https://corp.onda.me/post/developing-llm-applications-with-langchain
    • LangChain Library : Python 및 JavaScript 라이브러리. 수많은 구성 요소에 대한 인터페이스 및 통합, 이러한 구성 요소를 체인 및 에이전트로 결합하기 위한 기본 런타임, 체인 및 에이전트의 기성 구현이 포함
    • LangChain Template : 다양한 작업을 위해 쉽게 배포할 수 있는 참조 아키텍처 모음
    • LangServe: LangChain REST API로 배포하기 위한 라이브러리
    • LangSmith: LLM 프레임워크에 구축된 체인을 디버그, 테스트, 평가 및 모니터링하고 LangChain과 원활하게 통합할 수 있는 개발자 플랫폼
  • Pydantic
    • https://seoyeonhwng.medium.com/pydantic-살펴보기-27c67273f0be
    • pydantic은 파이썬 타입 어노테이션을 사용해서 데이터 유효성 검사와 설정 관리를 하는 라이브러리
    • pydantic은 validation이 아닌 parsing 라이브러리이기 때문에 input data를 정의된 타입으로 변환하여 output model의 타입과 제약 조건을 보장

 

 

 

IDE for Python 

 

부모 객체를 상속받아 여러 자식객체들로 이루어진 jsonString을 java backend에서 

VO로 mapping하고 싶은 경우가 있다. (deserialize)

 

 

다음과 같은 구조의 class들이 있다. 

public abstract class Animal{
 private String type;
 private String name;
}
public class Dog extends Animal{
 private int age;
 
 //getter , setter
}
public class Cat extends Animal{
 private boolean isHome;
 
 //getters, setters
}

 

Animal이 부모이고 Dog과 Cat이 자식이다.  Dog과 Cat 모두 Animal을 상속 받고 있다.

 

 

다음과 같은 json String이 있으면 이 code를 사용해서 vo객체에 mapping 해줄 수 있다.

 

public List<Animal> deserializeJsonStringToObj(String str){
 // example :  str = "[{\"name\":\"Milo\",\"age\":\"9\",\"type\":\"Dog\"},{\"name\":\"miyao\",\"isHome\":\"true\",\"type\":\"Cat\"}]";

 Type listOfAnimals = new TypeToken<ArrayList<Animal>>(){}.getType();
 RuntimeTypeAdapterFactory<Animal> adapter = RuntimeTypeAdapterFactory.of(Animal.class, "type")
      .registerSubtype(Dog.class)
      .registerSubtype(Cat.class);
      
Gson gson = new GsonBuilder().registerTypeAdapterFactory(adapter).create();
return gson.fromJson(str, listOfFilterJson);
}

 

 

 

RuntimeTypeAdapterFactory을 사용하는게 쉬운데,

gson에 RuntimeTypeAdapterFactory가 종종 없다고도 한다.

그러면 다음 파일을 불러와서 쓰면된다.

 

 

 

package com.sds.lowcode.data.functions;/*
 * Copyright (C) 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Adapts values whose runtime type may differ from their declaration type. This
 * is necessary when a field's type is not the same type that GSON should create
 * when deserializing that field. For example, consider these types:
 * <pre>   {@code
 *   abstract class Shape {
 *     int x;
 *     int y;
 *   }
 *   class Circle extends Shape {
 *     int radius;
 *   }
 *   class Rectangle extends Shape {
 *     int width;
 *     int height;
 *   }
 *   class Diamond extends Shape {
 *     int width;
 *     int height;
 *   }
 *   class Drawing {
 *     Shape bottomShape;
 *     Shape topShape;
 *   }
 * }</pre>
 * <p>Without additional type information, the serialized JSON is ambiguous. Is
 * the bottom shape in this drawing a rectangle or a diamond? <pre>   {@code
 *   {
 *     "bottomShape": {
 *       "width": 10,
 *       "height": 5,
 *       "x": 0,
 *       "y": 0
 *     },
 *     "topShape": {
 *       "radius": 2,
 *       "x": 4,
 *       "y": 1
 *     }
 *   }}</pre>
 * This class addresses this problem by adding type information to the
 * serialized JSON and honoring that type information when the JSON is
 * deserialized: <pre>   {@code
 *   {
 *     "bottomShape": {
 *       "type": "Diamond",
 *       "width": 10,
 *       "height": 5,
 *       "x": 0,
 *       "y": 0
 *     },
 *     "topShape": {
 *       "type": "Circle",
 *       "radius": 2,
 *       "x": 4,
 *       "y": 1
 *     }
 *   }}</pre>
 * Both the type field name ({@code "type"}) and the type labels ({@code
 * "Rectangle"}) are configurable.
 *
 * <h3>Registering Types</h3>
 * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
 * name to the {@link #of} factory method. If you don't supply an explicit type
 * field name, {@code "type"} will be used. <pre>   {@code
 *   RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory
 *       = RuntimeTypeAdapterFactory.of(Shape.class, "type");
 * }</pre>
 * Next register all of your subtypes. Every subtype must be explicitly
 * registered. This protects your application from injection attacks. If you
 * don't supply an explicit type label, the type's simple name will be used.
 * <pre>   {@code
 *   shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
 *   shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
 *   shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
 * }</pre>
 * Finally, register the type adapter factory in your application's GSON builder:
 * <pre>   {@code
 *   Gson gson = new GsonBuilder()
 *       .registerTypeAdapterFactory(shapeAdapterFactory)
 *       .create();
 * }</pre>
 * Like {@code GsonBuilder}, this API supports chaining: <pre>   {@code
 *   RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
 *       .registerSubtype(Rectangle.class)
 *       .registerSubtype(Circle.class)
 *       .registerSubtype(Diamond.class);
 * }</pre>
 *
 * <h3>Serialization and deserialization</h3>
 * In order to serialize and deserialize a polymorphic object,
 * you must specify the base type explicitly.
 * <pre>   {@code
 *   Diamond diamond = new Diamond();
 *   String json = gson.toJson(diamond, Shape.class);
 * }</pre>
 * And then:
 * <pre>   {@code
 *   Shape shape = gson.fromJson(json, Shape.class);
 * }</pre>
 */
public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
    private final Class<?> baseType;
    private final String typeFieldName;
    private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
    private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
    private final boolean maintainType;

    private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName, boolean maintainType) {
        if (typeFieldName == null || baseType == null) {
            throw new NullPointerException();
        }
        this.baseType = baseType;
        this.typeFieldName = typeFieldName;
        this.maintainType = maintainType;
    }

    /**
     * Creates a new runtime type adapter using for {@code baseType} using {@code
     * typeFieldName} as the type field name. Type field names are case sensitive.
     * {@code maintainType} flag decide if the type will be stored in pojo or not.
     */
    public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName, boolean maintainType) {
        return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName, maintainType);
    }

    /**
     * Creates a new runtime type adapter using for {@code baseType} using {@code
     * typeFieldName} as the type field name. Type field names are case sensitive.
     */
    public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
        return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName, false);
    }

    /**
     * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
     * the type field name.
     */
    public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
        return new RuntimeTypeAdapterFactory<T>(baseType, "type", false);
    }

    /**
     * Registers {@code type} identified by {@code label}. Labels are case
     * sensitive.
     *
     * @throws IllegalArgumentException if either {@code type} or {@code label}
     *     have already been registered on this type adapter.
     */
    public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
        if (type == null || label == null) {
            throw new NullPointerException();
        }
        if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
            throw new IllegalArgumentException("types and labels must be unique");
        }
        labelToSubtype.put(label, type);
        subtypeToLabel.put(type, label);
        return this;
    }

    /**
     * Registers {@code type} identified by its {@link Class#getSimpleName simple
     * name}. Labels are case sensitive.
     *
     * @throws IllegalArgumentException if either {@code type} or its simple name
     *     have already been registered on this type adapter.
     */
    public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
        return registerSubtype(type, type.getSimpleName());
    }

    public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
        if (type.getRawType() != baseType) {
            return null;
        }

        final Map<String, TypeAdapter<?>> labelToDelegate
            = new LinkedHashMap<String, TypeAdapter<?>>();
        final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate
            = new LinkedHashMap<Class<?>, TypeAdapter<?>>();
        for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
            TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
            labelToDelegate.put(entry.getKey(), delegate);
            subtypeToDelegate.put(entry.getValue(), delegate);
        }

        return new TypeAdapter<R>() {
            @Override public R read(JsonReader in) throws IOException {
                JsonElement jsonElement = Streams.parse(in);
                JsonElement labelJsonElement;
                if (maintainType) {
                    labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
                } else {
                    labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
                }

                if (labelJsonElement == null) {
                    throw new JsonParseException("cannot deserialize " + baseType
                        + " because it does not define a field named " + typeFieldName);
                }
                String label = labelJsonElement.getAsString();
                @SuppressWarnings("unchecked") // registration requires that subtype extends T
                    TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
                if (delegate == null) {
                    throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
                        + label + "; did you forget to register a subtype?");
                }
                return delegate.fromJsonTree(jsonElement);
            }

            @Override public void write(JsonWriter out, R value) throws IOException {
                Class<?> srcType = value.getClass();
                String label = subtypeToLabel.get(srcType);
                @SuppressWarnings("unchecked") // registration requires that subtype extends T
                    TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
                if (delegate == null) {
                    throw new JsonParseException("cannot serialize " + srcType.getName()
                        + "; did you forget to register a subtype?");
                }
                JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();

                if (maintainType) {
                    Streams.write(jsonObject, out);
                    return;
                }

                JsonObject clone = new JsonObject();

                if (jsonObject.has(typeFieldName)) {
                    throw new JsonParseException("cannot serialize " + srcType.getName()
                        + " because it already defines a field named " + typeFieldName);
                }
                clone.add(typeFieldName, new JsonPrimitive(label));

                for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
                    clone.add(e.getKey(), e.getValue());
                }
                Streams.write(clone, out);
            }
        }.nullSafe();
    }
}

 

 

 


참고

https://www.baeldung.com/gson-list

vue 에서 code Mirror를 쓰는 방법과

더나아가 MaxLength를 설정하는 방법을 알아보좌!

 

 

1. Code mirror 설치

npm install vue-codemirror --save

 그러면 package.json에 codemirror가 추가된걸 확인 할 수 있다.

 

2. main.js 에 선언

import Codemirror  from 'vue-codemirror';

Vue.use(Codemirror);

 

3. code mirror 컴포넌트 사용

<codemirror v-model="query" :options="options" ref="codeMirror" />

v-model에는 codemirror에 입력되는 값을 변수 선언한 뒤 넣어주고

다양한 모드와 옵션이 있는데 그건 부모 컴포넌트에서 options 로 선언해주고 props로 내려주면된다.

 

나같은 경우에는 query를 입력하는 창이라 관련되어서 옵션을 data에 선언해 주었다.

 

예)

                options: {
                    tabSize: 4,
                    styleActiveLine: true,
                    mode: 'text/x-sql',
                    lineNumbers: true,
                    line: true,
                    lineWrapping: true,
                    theme: 'default'
                },

 

더 다양한 옵션은 다음을 참고해보자

https://github.com/surmon-china/vue-codemirror#readme

 

GitHub - surmon-china/vue-codemirror: ⌨️ @codemirror component for @vuejs

⌨️ @codemirror component for @vuejs. Contribute to surmon-china/vue-codemirror development by creating an account on GitHub.

github.com

 

4. max Length 설정하기

options에 따로 max Length 관련 지원이 없어서 직접 구현하였다.

 

먼저 기존 options에 maxLength라고 선언해준다.

                options: {
                    tabSize: 4,
                    styleActiveLine: true,
                    mode: 'text/x-sql',
                    lineNumbers: true,
                    line: true,
                    lineWrapping: true,
                    theme: 'default',
                    maxLength: 4000,
                },

 

그 뒤 , 다음 링크를 참고하여 구현 하였다.

https://github.com/codemirror/CodeMirror/issues/821

 

MaxLength attribute · Issue #821 · codemirror/CodeMirror

Is there a way to set a MaxLength number of characters allowed?

github.com

 

 

codemirror 소스를 보니 모든 event가 발생하면 emit 해주고 있었다.

 

        const allEvents = [
          'scroll',
          'changes',
          'beforeChange',
          'cursorActivity',
          'keyHandled',
          'inputRead',
          'electricInput',
          'beforeSelectionChange',
          'viewportChange',
          'swapDoc',
          'gutterClick',
          'gutterContextMenu',
          'focus',
          'blur',
          'refresh',
          'optionChange',
          'scrollCursorIntoView',
          'update'
        ]
        .concat(this.events)
        .concat(this.globalEvents)
        .filter(e => (!tmpEvents[e] && (tmpEvents[e] = true)))
        .forEach(event => {
          // 循环事件,并兼容 run-time 事件命名
          this.cminstance.on(event, (...args) => {
            // console.log('当有事件触发了', event, args)
            this.$emit(event, ...args) // EMIT !!!!
            const lowerCaseEvent = event.replace(/([A-Z])/g, '-$1').toLowerCase()
            if (lowerCaseEvent !== event) {
              this.$emit(lowerCaseEvent, ...args)
            }
          })
        })

 

그래서 부모 컴포넌트에서 codeMirror를 사용하는쪽에서 emit 하는 function을 받아주었다.

<codemirror v-model="query" :options="options" ref="codeMirror" @beforeChange="enforceMaxLength" />

 그리고 다음과 같이 더이상 입력되지 않도록 function 을 선언해주었다. 

            enforceMaxLength(cm, change) {
                let maxLength = cm.getOption('maxLength');
                if (maxLength) {
                    let str = change.text.join('\n');
                    let delta = str.length - (cm.indexFromPos(change.to) - cm.indexFromPos(change.from));
                    this.cmLength = cm.getValue().length + delta;
                    if (this.cmLength > maxLength) {
                        this.cmLength = maxLength;
                    }
                    delta = cm.getValue().length + delta - maxLength;
                    if (delta >= 0) {
                        str = str.substr(0, str.length - delta);
                        change.update(change.from, change.to, str.split('\n'));
                    }
                }
                return true;
            },

 

그리고 현재 length를 세어주기 위해 cmLength를 선언해주고 length를 넣어주었다. 

 


Test Double 이란?

실제 객체를 대신해서 테스팅에서 사용하는 모든 방법을 일컬어 호칭한다. 

Java 진영에서는 대표적으로 Mockito가 있습니다.



Mockito의 어노테이션

@Mock
@MockBean
@Spy
@SpyBean
@InjectMocks



1. Java의 test double : Mockito

1. @Mock

Mockito.mock() 코드를 대체

@Mock으로 mock 객체 생성


 

1.2 @InjectMocks

해당 클래스가 필요한 의존성과 맞는 Mock 객체들을 감지하여 , 해당 클래스의 객체가 만들어질때 사용하여

객체를 만들고 해당변수에 객체를 주입하게된다.

1.2 @Spy

- 실제 객체의 스파이를 생성하여 실제 객체의 메소드를 호출 할 수 있게 합니다.

- stub 하면 stub 하는 객체 , 아니면 실제 객체를 호출 합니다. 
- 하나의 객체를 선택적으로 stub 할 수 있도록 하는 기능 

- mockito.spy()도 사용가능

- When Returns 해서 어떤값이 들어갔을때 해당 값이 리턴되도록 미리 선언해둔다

이유는.. 해당 method는 부가적인 기능이라 중점적인 기능을 test 하기위해 미리 선언해두는 것을 stubbing이라고 한다.

- 둘의 가장 큰 차이점은 @Spy 실제 인스턴스를 사용해서 mocking을 하고, @Mock은 실제 인스턴스 없이 가상의 mock 인스턴스를 직접 만들어 사용한다는 것이다. 그래서 @Spy Mockito.when() 이나 BDDMockito.given() 메서드 등으로 메서드의 행위를 지정해 주지 않으면 @Spy 객체를 만들 때 사용한 실제 인스턴스의 메서드를 호출한다.

 

stubbing 예제

// stubbing
when(mockedList.get(0)).thenReturn("ok");
when(mockedList.get(1)).thenThrow(new RuntimeException());

 

- @Spy 는 객체 instance의 초기화를 해주어야한다 

@Spy
List<String> spyList = new ArrayList<String>(); //초기화

@Test
public void whenUsingTheSpyAnnotation_thenObjectIsSpied() {
    spyList.add("one");
    spyList.add("two");

    Mockito.verify(spyList).add("one");
    Mockito.verify(spyList).add("two");

    assertEquals(2, spyList.size());
}



2. SpringBootTest의 Test double


1. @MockBean


@MockBean은 스프링 컨텍스트에 mock객체를 등록하게 되고 스프링 컨텍스트에 의해 @Autowired가 동작할 때 등록된 mock객체를 사용할 수 있도록 동작합니다.

 


- Spring 영역의 어노테이션
- @Mock은 @InjectMocks에 대해서만 해당 클래스안에서 정의된 객체를 찾아서 의존성을 해결합니다.
- @MockBean은 mock 객체를 스프링 컨텍스트에 등록하는 것이기 때문에 @SpringBootTest를 통해서 Autowired에 의존성이 주입되게 됩니다.

- @Autowired라는 강력한 어노테이션으로 컨텍스트에서 알아서 생성된 객체를 주입받아 테스트를 진행할 수 있도록 합니다.

Mock 종류

의존성 주입
@Mock @InjectMocks 
@MockBean @Autowired

 

 


2. @SpyBean

- @MockBean과 마찬가지로 스프링 컨테이너에 Bean으로 등록된 객체에 대해 Spy를 생성

- @SpyBean이 Interface일 경우 구현체가 반드시 Spring Context에 등록되어야 합니다. => 등록되지 않은 상태라면, @MockBean을 사용하는 것이 좋은 방법이 될 수 있습니다.

- @SpyBean은 실제 구현된 객체를 감싸는 프록시 객체 형태이기 때문에 스프링 컨텍스트에 실제 구현체가 등록되어 있어야 합니다.


참고

https://cobbybb.tistory.com/16

https://www.baeldung.com/mockito-spy

https://velog.io/@june0313/Mockito-Mock-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%A3%BC%EC%9E%85%ED%95%98%EA%B3%A0-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%95%98%EA%B8%B0

vue 개발을 하다가 data의 값이 잘 바꼈는데

컴포넌트 rerendering이 잘안돼서 그대로 이상한 값이 남아 있는 경우가 있었다.

 

그래서 찾아보니 좋은 stackOverFlow를 발견..

강제로 rerendering을 시켜주고 싶었다!

 

https://stackoverflow.com/questions/32106155/can-you-force-vue-js-to-reload-re-render

 

Can you force Vue.js to reload/re-render?

Just a quick question. Can you force Vue.js to reload/recalculate everything? If so, how?

stackoverflow.com

 

 


이중에 어떤 방법을 할까하다가

 

처음엔 당연히

this.$forceUpdate()를 쓰려고 했지만 잘안돼서.. 그치만 다시 찬찬히 읽어보니 방법이 생각났지만

여기 나온 방식중에서 가장 Best way라고 나온  key를 이용하는 방식을 써서 해결하였다.

 

그럼 forceUpdate와 Key를 이용해서 강제 rerendering하는 것을 알아보자 

(https://michaelnthiessen.com/force-re-render/ 다음을 해석하였습니다. 잘쓰여져있네여)

 


1. forceUpdate();

이것은 vue에서 공식적으로 제공해주는 방식이다. (https://vuejs.org/v2/api/#vm-forceUpdate)

 

보통은 Vue에서 data의 변화를 감지하는데 이것을 보통 reactive 하다고 말합니다. 

그런데 아시다시피 Vue의 reactivity 시스템은 모든 변화를 감지하지 못합니다..(언제그러는지는 정확하게 모르겠으나 개발하다보면 왕왕 있습니다..)

 

그래서 변화를 감지하지 못할때 강제 리렌더링을 해줍니다.

 

forceUpdate를 하기위해서는두가지 방법이 있는데 , Global하게 forceUpdate 하는 것과component 단위로 forceUpdate 하는방법

 

단순하게 생각해도 Global한 방식은 잘안쓸거 같군요..

// Globally
import Vue from 'vue';
Vue.forceUpdate();

// Using the component instance
export default {
  methods: {
    methodThatForcesUpdate() {
      // ...
      this.$forceUpdate();  // Notice we have to use a $ here
      // ...
    }
  }
}

 

 

2. key 이용

 

key를 이용 하려면 Vue에서 제공하는 key에 대해서 알아야 하는데

 

Vue에서 key는 각 특정 component에 특정 값을 매핑해 두는데 이게 key라고 볼 수 있다.

그래서, key가 동일하면 component가 변하지 않지만

key가 변하면 Vue는 예전 component 를 지우고 새로운 component를 만듭니다.

 

 

<template>
   <component-to-re-render :key="componentKey" />
</template>

<script>
 export default {
  data() {
    return {
      componentKey: 0,
    };
  },
  methods: {
    forceRerender() {
      this.componentKey += 1;  
    }
  }
 }
</script>

그래서 다음과같이 자식 component에 key를 props로 내려주고

forceRerender라는 method에 key값을 변경해주도록 해주면

 

강제 Rerendering이 필요할때 forceRerender() method를 호출해주면

Vue에서 이전 component 를 지우고 새 component를 그려줍니다.

 

저도 이방법으로 쓰레기값이 남아있는 문제를 해결하였습니다!

 

굳굳!

 

 

+ Recent posts