<p>apollo-link-error ν˜ΈμΆœμžμ—κ²Œ 였λ₯˜ λ°˜ν™˜</p>

에 λ§Œλ“  2019λ…„ 04μ›” 15일  Β·  17μ½”λ©˜νŠΈ  Β·  좜처: apollographql/apollo-link

κ·Έλž˜μ„œ μ €λŠ” Apollo-link-errorλ₯Ό μ‚¬μš©ν•˜μ—¬ auth 및 admin μ „μ—­ 였λ₯˜λ₯Ό κ΄€λ¦¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 그것을 μ‚¬μš©ν•˜λ©΄ λ‚΄ μ•½μ†μ˜ catch λ©”μ„œλ“œκ°€ 였λ₯˜λ₯Ό λ°˜ν™˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

λͺ¨λ“  였λ₯˜κ°€ apollo-link-errorλ₯Ό 거쳐 호좜자 λ©”μ„œλ“œλ‘œ λ‹€μ‹œ μ „λ‹¬λ˜μ§€ μ•ŠλŠ” κ²ƒμ²˜λŸΌ λ³΄μž…λ‹ˆλ‹€.

일뢀 였λ₯˜λŠ” λ‘œμ»¬μ—μ„œ κ΄€λ¦¬ν•˜κ³  일뢀 였λ₯˜λŠ” μ „μ—­μ μœΌλ‘œ 관리할 수 μžˆλ„λ‘ ν˜ΈμΆœμžμ—κ²Œ 였λ₯˜λ₯Ό λ°˜ν™˜ν•˜λŠ” 방법이 μžˆμŠ΅λ‹ˆκΉŒ?

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

μ΄μƒν•œ 해결책을 μ°Ύμ•˜μ§€λ§Œ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

이것은 λ©”μ‹œμ§€λ₯Ό μΈμ‡„ν•©λ‹ˆλ‹€

  updateProfile: function() {
      this.$apollo
        .mutate({
          mutation: UPDATE_PROFILE,
          variables: current_user
        })
        .catch((error) => {
          console.log("this prints just the message", error);
        });
    }

κ·ΈλŸ¬λ‚˜ 이것은 였λ₯˜μ˜ 전체 λ‚΄μš©μ„ μΈμ‡„ν•©λ‹ˆλ‹€.

  updateProfile: function() {
      this.$apollo
        .mutate({
          mutation: UPDATE_PROFILE,
          variables: current_user
        })
        .catch(({ graphQLErrors }) => {
          console.log("this prints the full content of 'errors'", graphQLErrors);
        });
    }

이 λ¬Έμ œλŠ” 닫을 수 μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€
@Nosherwan @romucci @Sceat @lbrdar

λͺ¨λ“  17 λŒ“κΈ€

λ‚΄ 앱에도 같은 λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. 였λ₯˜ 링크의 λͺ¨λ“  였λ₯˜λ₯Ό ν¬μ°©ν•˜κ³  μ‹Άμ§€λ§Œ(예λ₯Ό λ“€μ–΄ 기둝할 수 μžˆλ„λ‘) 였λ₯˜λ₯Ό μ›λž˜ ν˜ΈμΆœμžμ—κ²Œ μ „λ‹¬ν•˜κ³  μ‚¬μš©μžμ—κ²Œ ν‘œμ‹œν•΄μ•Ό ν•˜λŠ” μ‚¬μš©μž 지정 였λ₯˜ λ©”μ‹œμ§€κ°€ 포함될 수 μžˆμœΌλ―€λ‘œ κ±°κΈ°μ—μ„œ μ²˜λ¦¬ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€. .

λ‚΄ μ½”λ“œ:

  • μ œκ³΅μžμ—μ„œ:
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, locations, path }) => {
        console.log(`[GraphQL error]: ${message}, Location: ${locations}, Path: ${path}`);
      },
    );
  }
});

const client = new ApolloClient({
  link: authLink.concat(errorLink).concat(httpLink),
  cache: new InMemoryCache(),
});

  • ꡬ성 μš”μ†Œμ—μ„œ:
 loginMutation({ variables: { data } })
    .then(({ data, errors }) => {
      if (errors) {
        // I WANT TO BE ABLE TO READ ERROR MESSAGE HERE SO I CAN DISPLAY IT TO THE USER
      } else if (data) {
        ....
      }
    });

forward λ₯Ό ν˜ΈμΆœν•˜λ©΄ then λŒ€ν•œ 인수둜 undefinedλ₯Ό λ°˜ν™˜ν•˜λ―€λ‘œ 데이터도 였λ₯˜λ„ μ—†κ³  errors λ₯Ό null μ„€μ •ν•˜λ―€λ‘œ 도움이 λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€

@lbrdar 이에 λŒ€ν•œ ν•΄κ²° 방법을 μ°Ύμ•˜μŠ΅λ‹ˆκΉŒ?

μ•„λ‹ˆ 😞

@lbrdar 이것을 ν•˜λŠ” 방법은 캐치(catch)λ₯Ό μ‚¬μš©ν•˜λŠ” 것 μ•„λ‹™λ‹ˆκΉŒ?

 loginMutation({ variables: { data } })
    .then(({ data, errors }) => {
      if (errors) {
        // I WANT TO BE ABLE TO READ ERROR MESSAGE HERE SO I CAN DISPLAY IT TO THE USER
      } else if (data) {
        ....
      }
    }).catch(errors => {

    });

@lbrdar @romucci 저도 같은 λ¬Έμ œμ— μ§λ©΄ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. μ—¬λŸ¬λΆ„, κ²°κ΅­ ν•΄κ²°ν•˜μ…¨λ‚˜μš”?

@Nosherwan 방금 버전 1.1.11을 μ„€μΉ˜ν–ˆκ³  이제 λͺ¨λ“  것이 잘 μž‘λ™ν•©λ‹ˆλ‹€... λ‚΄λΆ€ 약속을 확인할 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€(μ–΄λ”˜κ°€ λ‹€λ₯Έ 약속 μ•ˆμ— 약속을 λ°˜ν™˜ν•˜λŠ” 것을 μžŠμ—ˆμ„ μˆ˜λ„ 있음)

@merksam λ‹˜, λŒ“κΈ€ κ°μ‚¬ν•©λ‹ˆλ‹€.
λ‚˜λŠ” λ‹Ήμ‹ κ³Ό λ˜‘κ°™μ€ 버전을 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. λΆˆν–‰νžˆλ„ λ™μž‘μ€ λ™μΌν•©λ‹ˆλ‹€.

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, locations, path }) => {
        console.log(`[GraphQL error]: ${message}, Location: ${locations}, Path: ${path}`);
      },
    );
  }
});

μœ„μ˜ μ½”λ“œ λΈ”λ‘μ—μ„œ graphQLErrorsκ°€ 포착되고 onError ν•Έλ“€λŸ¬μ—μ„œ λ­”κ°€λ₯Ό ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ΄λŸ¬ν•œ 였λ₯˜λŠ” μ‹€μ œ 호좜 μ•½μ†μœΌλ‘œ μ „λ‹¬λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
λ‚˜λŠ” 비동기 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜κ³  μžˆμœΌλ―€λ‘œ 'then' λŒ€μ‹  await ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜κ³  있으며 μ΄λŠ” try catch 블둝 μ•ˆμ— μžˆμŠ΅λ‹ˆλ‹€.
catch 블둝은 였λ₯˜λ₯Ό ν¬μ°©ν•˜μ§€λ§Œ μ „λ‹¬λœ 였λ₯˜λŠ” μ—†μŠ΅λ‹ˆλ‹€.

λˆ„κ΅°κ°€ 이에 λŒ€ν•œ ν•΄κ²° 방법을 μ°Ύμ•˜μŠ΅λ‹ˆκΉŒ?

@Nosherwan @prem-prakash μ±„νŒ…μœΌλ‘œ μ—°λ½ν•˜κ±°λ‚˜ μ „ν™”λ₯Ό κ±Έμ–΄ λ‚΄ κ²½μš°μ—λŠ” μž‘λ™ν•˜κ³  κ·€ν•˜μ˜ κ²½μš°μ—λŠ” μž‘λ™ν•˜μ§€ μ•ŠλŠ” 이유λ₯Ό μ•Œμ•„λ‚΄λ €κ³  ν•  수 μžˆμŠ΅λ‹ˆκΉŒ? (μ—¬μ „νžˆ 관련성이 μžˆλŠ” 경우)

μ΄μƒν•œ 해결책을 μ°Ύμ•˜μ§€λ§Œ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

이것은 λ©”μ‹œμ§€λ₯Ό μΈμ‡„ν•©λ‹ˆλ‹€

  updateProfile: function() {
      this.$apollo
        .mutate({
          mutation: UPDATE_PROFILE,
          variables: current_user
        })
        .catch((error) => {
          console.log("this prints just the message", error);
        });
    }

κ·ΈλŸ¬λ‚˜ 이것은 였λ₯˜μ˜ 전체 λ‚΄μš©μ„ μΈμ‡„ν•©λ‹ˆλ‹€.

  updateProfile: function() {
      this.$apollo
        .mutate({
          mutation: UPDATE_PROFILE,
          variables: current_user
        })
        .catch(({ graphQLErrors }) => {
          console.log("this prints the full content of 'errors'", graphQLErrors);
        });
    }

이 λ¬Έμ œλŠ” 닫을 수 μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€
@Nosherwan @romucci @Sceat @lbrdar

이것을 λ¬Έμ„œμ— μΆ”κ°€ν•˜λŠ” 것은 μ–΄λ–»μŠ΅λ‹ˆκΉŒ?

μ£„μ†‘ν•˜μ§€λ§Œ μœ„μ˜ μ†”λ£¨μ…˜μ΄ μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 링크 였λ₯˜μ˜ graphqlErrorsλ₯Ό 초기 ν˜ΈμΆœμžμ—κ²Œ λ‹€μ‹œ λ³΄λ‚΄λŠ” 방법에 λŒ€ν•œ 전체 예λ₯Ό μƒκ°ν•©λ‹ˆλ‹€.

ν•„μžμ˜ 경우 였λ₯˜κ°€ λ°œμƒν•˜λ©΄ λ‹€μŒ ApolloQueryResult 객체가 onErrorμ—μ„œ μ½˜μ†”λ§λœ 였λ₯˜ μ„ΈλΆ€ 정보λ₯Ό 얻지 λͺ»ν•©λ‹ˆλ‹€. μ‹œλ„ 쀑 μΌλΆ€λŠ” ... 톡화λ₯Ό λ‘˜λŸ¬μ‹Έκ³  μžˆμŠ΅λ‹ˆλ‹€. μ„œλ²„μ—μ„œ graphqlError μ„ΈλΆ€ 정보λ₯Ό κ°€μ Έμ˜¬ 수 μ—†μŠ΅λ‹ˆλ‹€. κ·Έλƒ₯ "400 였λ₯˜"..

const gqlResult: ApolloQueryResult<IGReturnData<
            IAllDataTypes
        >> = await apolloClient.query<IGReturnData<IAllDataTypes>, TVariables>({
            query: queryGql,
            variables: queryVariables,
            errorPolicy: "all",
        });

μ„œλ²„ ꡬ성:

const errorLink = onError(({ graphQLErrors, networkError }) => {
    if (graphQLErrors)
        graphQLErrors.forEach(({ message, locations, path }) =>
            console.log(
                `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
            ),
        );

    if (networkError) console.log(`[Network error]: ${networkError}`);
});

const httplink = new HttpLink({
    uri: "/graphql",
    credentials: "include",
});

const links = [errorLink, httplink];

export const apolloClient = new ApolloClient({
    link: ApolloLink.from(links),
    cache: new InMemoryCache({ addTypename: false, fragmentMatcher }),
});

@prem-prakash κ°μ‚¬ν•©λ‹ˆλ‹€.... κ΅¬μ›μž..

이 λ¬Έμ œλŠ” 맀우 μ§€μ†μ μž…λ‹ˆλ‹€. λ‚΄ ν”„λ‘œμ νŠΈμ˜ graphql μ„œλ²„λŠ” μ œμ–΄ν•  수 μ—†κ³  ν΄λΌμ΄μ–ΈνŠΈλ§Œ μ œμ–΄ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ„œλ²„λŠ” μ˜¬λ°”λ₯Έ λͺ¨μ–‘μ˜ graphql 였λ₯˜λ‘œ μ‘λ‹΅ν•˜μ§€λ§Œ μƒνƒœ μ½”λ“œλŠ” 400μž…λ‹ˆλ‹€. 였λ₯˜ λ§ν¬μ—μ„œ graphQLErrors μ•‘μ„ΈμŠ€ν•  수 μžˆμ§€λ§Œ ꡬ성 μš”μ†Œ λ³€ν˜•μ—μ„œ @prem-prakashμ—μ„œ μ œμ•ˆν•œ λŒ€λ‘œ graphQLErrors λ₯Ό λΉΌλ‚Ό λ•Œ graphQLErrors λŠ” 빈 λ°°μ—΄μž…λ‹ˆλ‹€. . κΈ°λ³Έ λ©”μ‹œμ§€ "였λ₯˜: λ„€νŠΈμ›Œν¬ 였λ₯˜: 응닡 μ‹€νŒ¨: μˆ˜μ‹  μƒνƒœ μ½”λ“œ 400"μ—λ§Œ μ•‘μ„ΈμŠ€ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ApolloλŠ” 400 μƒνƒœ μ½”λ“œκ°€ Apollo의 λ§ˆμΉ¨ν‘œμ΄κΈ° λ•Œλ¬Έμ— μ„œλ²„μ—μ„œ μ‚¬λžŒμ΄ 읽을 수 μžˆλŠ” 였λ₯˜ λ©”μ‹œμ§€("μ‚¬μš©μž λ˜λŠ” μ•”ν˜Έκ°€ μ˜¬λ°”λ₯΄μ§€ μ•ŠμŒ")λ₯Ό λ°©ν•΄ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

였λ₯˜ λ©”μ‹œμ§€κ°€ ν¬ν•¨λœ 400 μƒνƒœ μ½”λ“œ 응닡을 μ„±κ³΅μ μœΌλ‘œ μ²˜λ¦¬ν•˜κ³  ν•΄λ‹Ή λ©”μ‹œμ§€λ₯Ό λŒμ—°λ³€μ΄λ₯Ό ν˜ΈμΆœν•œ ꡬ성 μš”μ†Œμ˜ UI에 전달할 수 μžˆλŠ” μ‚¬λžŒμ΄ μžˆμŠ΅λ‹ˆκΉŒ?

μ’‹μ•„, λ‚˜λŠ” κ½€ λΆˆν•©λ¦¬ν•œ μ ‘κ·Ό 방식을 가지고 μžˆμ§€λ§Œ μž‘λ™ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

μΊμ‹œμ— λΆ€μšΈ hasGraphError λ₯Ό μ„€μ •ν•˜κ³  였λ₯˜ λ©”μ‹œμ§€λ„ μΊμ‹œν•©λ‹ˆλ‹€.

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(error => {
      // log gql error(s)
      console.log("[GraphQL error]: ", error);
      // cache error
      client.writeData({
        data: {
          hasGraphError: true,
          currentGraphError: error.message
        }
      });
    });
  }
  if (networkError) {
    // log network errors
    console.log("[Network error]: ", networkError);
  }
});

그런 λ‹€μŒ MutationError ꡬ성 μš”μ†Œμ—μ„œ μΊμ‹œμ— 였λ₯˜ 및 였λ₯˜ λ©”μ‹œμ§€κ°€ μžˆλŠ”μ§€ μΏΌλ¦¬ν•˜κ³  μ‘°κ±΄λΆ€λ‘œ gql 였λ₯˜ λ˜λŠ” μ‹€μ œ λ„€νŠΈμ›Œν¬ 였λ₯˜λ₯Ό λ Œλ”λ§ν•©λ‹ˆλ‹€.

const HAS_ERROR = gql`
  query IsGraphErrorPresent {
    hasGraphError <strong i="11">@client</strong>
    currentGraphError <strong i="12">@client</strong>
  }
`;
export default function MutationError({ error }) {
  const { data } = useQuery(HAS_ERROR);
  const defaultErrorMessage =
    "We're having trouble connecting. Please check your internet connection and try again.";

  // real network error
  if (error && error.message.includes("Failed to fetch")) {
    return <Error>{defaultErrorMessage}</Error>;
  }

  // graph error
  if (error && data && data.hasGraphError) {
    return <Error>{data.currentGraphError}</Error>;
  }

  // probably a real server/network error
  if (error) {
    return <Error>{defaultErrorMessage}</Error>;
  }

  return null;
}

λ‚΄ μ„œλ²„κ°€ _항상_ 200 + graphql 였λ₯˜κ°€ λ˜μ–΄μ•Ό ν•˜λŠ” 것에 λŒ€ν•΄ 400을 λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ— 이것은 λͺ¨λ“  λŒμ—°λ³€μ΄μ— ν•„μš”ν•˜κΈ° λ•Œλ¬Έμ— 전역적일 κ²ƒμž…λ‹ˆλ‹€(λ‚˜λŠ” 그것에 λŒ€ν•΄ μ•½κ°„ μ§ ν•©λ‹ˆλ‹€)...

μ—¬κΈ°μ„œ μ€‘μš”ν•œ 점은 λͺ¨λ“  ꡬ성 μš”μ†Œ λ³€ν˜•μ—μ„œ Apollo의 μ²˜λ¦¬λ˜μ§€ μ•Šμ€ μ˜ˆμ™Έλ₯Ό λ°©μ§€ν•˜λŠ” 빈 onError μ½œλ°±μ„ μ‚¬μš©ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. μ„±κ³΅ν•˜λ©΄ μΊμ‹œμ—μ„œ hasGraphError λΆ€μšΈμ„ μž¬μ„€μ •ν•΄μ•Ό ν•œλ‹€λŠ” 것을 κΈ°μ–΅ν•΄μ•Ό ν•©λ‹ˆλ‹€.

  const [someMutation, { loading, error, client }] = useMutation(SOME_MUTATION, {
    onError() {
      // this callback prevents apollo from throwing
      // ...unhandled exception on 400 status code
    },
    onCompleted({ someMutation }) {
      client.writeData({
        data: {
          hasGraphError: false
        }
      });
    }
  });

그런 λ‹€μŒ λŒμ—°λ³€μ΄ 였λ₯˜ ꡬ성 μš”μ†ŒλŠ” useMutation 였λ₯˜λ₯Ό μ†Œν’ˆμœΌλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€(λ‘˜ λ‹€ μ‹€μ œ λ„€νŠΈμ›Œν¬ 였λ₯˜λ₯Ό κ²°μ •ν•  수 있게 ν•˜κ³  잘λͺ»λœ ꡬ성 μš”μ†Œμ—μ„œ μ „μ—­ μΊμ‹œλœ gql 였λ₯˜λ₯Ό λ Œλ”λ§ν•˜μ§€ μ•Šλ„λ‘ ν•©λ‹ˆλ‹€).

  {loading && <Spinner />}
  {error && <MutationError error={error} />}

λ‚΄κ°€ λ§ν–ˆλ“―μ΄ 이 μ ‘κ·Ό 방식은 맀우 λΉ„ν•©λ¦¬μ μ΄μ§€λ§Œ ν˜„μž¬ ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ…Έλ ₯ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

  1. 응닡에 200이 μ•„λ‹Œ μƒνƒœ μ½”λ“œκ°€ μžˆλŠ” κ²½μš°μ—λ„ 일반적으둜 UIμ—μ„œ graphql 였λ₯˜λ₯Ό μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  2. 특히 UIμ—μ„œ λ Œλ”λ§ν•˜κΈ° μœ„ν•΄ apollo-link-error κ΅¬μ„±μ—μ„œ 호좜 ꡬ성 μš”μ†Œλ‘œ 였λ₯˜λ₯Ό 전달할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ½”λ”©λœ 이 μ ‘κ·Ό λ°©μ‹μ˜ 문제: μ΄λŠ” μ–΄λ ˆμ΄μ˜ λ§ˆμ§€λ§‰ GQL 였λ₯˜λ₯Ό μΊμ‹œμ— κΈ°λ‘ν•˜λ―€λ‘œ λ™μ‹œμ— μ—¬λŸ¬ GQL 였λ₯˜λ₯Ό μ§€μ›ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 이것은 였λ₯˜ 배열을 μž‘μ„±ν•˜κ³  이λ₯Ό μΊμ‹œμ— μ €μž₯ν•˜μ—¬ κ΄€λ¦¬ν•˜κΈ°κ°€ μƒλ‹Ήνžˆ μ‰¬μ›Œμ•Ό ν•˜μ§€λ§Œ 이λ₯Ό μˆ˜ν–‰ν•˜λ €λ©΄ 둜컬 μŠ€ν‚€λ§ˆ/리쑸버λ₯Ό μ •μ˜ν•˜κ±°λ‚˜ 잠재적으둜 JSON.stringifyλ₯Ό μ‚¬μš©ν•˜μ—¬ μ €μž₯ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ¬Έμžμ—΄λ‘œ μΊμ‹œν•©λ‹ˆλ‹€. λ˜ν•œ λΆ€μšΈ 값을 false둜 μ„€μ •ν•˜λŠ” λŒ€μ‹  성곡 μ‹œ μΊμ‹œμ—μ„œ currentGraphErrorλ₯Ό μ§€μ›Œμ•Ό ν•©λ‹ˆλ‹€.

그것이 λˆ„κ΅°κ°€λ₯Ό 돕기λ₯Ό λ°”λžλ‹ˆλ‹€!

λ˜ν•œ 도움이 λ˜λŠ” 경우 errorPolicyλŠ” ν˜„μž¬ useMutation ν›„ν¬μ—μ„œ μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 이것은 버그이며 졜근 이 PRμ—μ„œ ν•΄κ²°λ˜μ—ˆμŠ΅λ‹ˆλ‹€: https://github.com/apollographql/apollo-client/pull/5863 ν•˜μ§€λ§Œ ν˜„μž¬ μΆœμ‹œλ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

이에 λŒ€ν•œ 개발자의 진전 사항이 μžˆμŠ΅λ‹ˆκΉŒ?

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰