Skip to content

Latest commit

 

History

291 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CandyKorean

외국인들을 위한 한국어 학습 플랫폼

image

목차

프로젝트 설명

  • 총 3개의 난이도로 수업 레벨이 구성되어 있고, 각 레벨에는 튜터와 각 튜터가 맡은 코스가 있습니다. 사용자는 원하는 코스를 선택하여 코스의 강의를 수강한 후, 퀴즈를 통해 본인의 한국어 능력을 시험해볼 수 있습니다. 또한, Premium 과정을 통해 튜터와의 1대1 Private Class를 수강할 수 있습니다.

  • 구글 개발자 계정을 통해 완성된 앱을 플레이스토어의 심사를 거친 후에 배포하였습니다. 인앱 결제 기능을 구현하면서, JavaScript의 콜백함수, async 와 await 의 활용력을 기를 수 있었고, 실제 앱 배포를 통해 안드로이드 앱 개발에 필요한 빌드 능력을 갖추게 되었습니다.

기술 스택

  • React Native 프레임워크를 활용하여 프론트엔드 개발 환경을 구축하였고, Node.js 을 사용하여 백엔드 API 을 만들었고 클라이언트 환경과 통신하는 로직을 구현하였습니다.

image
Google Play Console에 앱 등록

image
인앱 상품 메뉴에 등록한 결제 가능 강의들

  • 인앱 결제 프로세스 구현을 위해 Google Play ConsoleExpo 라이브러리를 통해 빌드한 앱 결과물을 등록하였습니다. 그 후, 결제 가능한 강의들을 인앱 상품 에 등록한 후, InAppPurchases 기능과 연동하여 결제가 성공적으로 완료되도록 하였습니다.

구현 핵심 기능 1 : 인앱 결제 오류 해결

default.mp4

인앱 결제 시연 영상

...
...
try {
    
     // 구매 정보 가져오기
     const { responseCode, results } = await getProductsAsync(itemArray);

     if (responseCode === IAPResponseCode.OK) {
         setProductId(results[0].productId)
         setBottomText(results[0].productId);
         
     } else {
      setBottomText('something wrong!');
     }

     // 구매 내역에 없는 상품일때만 결제 진행
     if(purchasedCourseList.indexOf(itemInfo.course_id) === -1) {
      purchaseItemAsync(results[0].productId)
     } else {
      // 아닐떈, 바로 이미 구입한 상품이라고 알려주기
      navigation.navigate("PaymentResult", {
        user_id: userId,
        itemInfo: itemInfo,
        isSuccess: false,
        returnToClass,
        imgUrl: imgUrl,
        isBought: true,
      });
     }

     return await new Promise((resolve, reject) => {
      setPurchaseListener(async (result) => {
        if(result.responseCode === IAPResponseCode.OK){
          setBottomText("success")
          if(!result.results[0].acknowledged) {
            setBottomText('successful purchase')
            await finishTransactionAsync(result.results[0], false);

            // 계속 구매 가능한지 test 
            // await finishTransactionAsync(result.results[0], true);

            // DB에 저장 - purchasedCourse 에 없는 경우에만!
            if(purchasedCourseList.indexOf(itemInfo.course_id) === -1) {
            createPurchasedCourse(
              { user_id: userId, course_id: itemInfo.course_id },
              (d) => {
      
                navigation.navigate("PaymentResult", {
                  user_id: userId,
                  itemInfo: itemInfo,
                  isSuccess: true,
                  returnToClass,
                  imgUrl: imgUrl,
                });
              },
              setIsCoursePurchased,
              (e) => {
                setIsSuccess(false);
                console.log(e.message);
              }
            );
            } else {
              navigation.navigate("PaymentResult", {
                user_id: userId,
                itemInfo: itemInfo,
                isSuccess: true,
                returnToClass,
                imgUrl: imgUrl,
              });
            }
        }
        } else if (result.responseCode === IAPResponseCode.USER_CANCELED || result.responseCode === IAPResponseCode.DEFERRED) {
          setBottomText('User canceled the transaction');

        }  else {
          
          setBottomText(`Something went wrong with the purchase. Received errorCode ${result.errorCode}`);
            navigation.navigate("PaymentResult", {
            user_id: userId,
            itemInfo: itemInfo,
            isSuccess: false,
            returnToClass,
            imgUrl: imgUrl,
            isBought: result.errorCode === 8 ? true : false,
          });
        }
      })

    })

    } catch(e) {
      disconnectAsync();
      setBottomText('error!!!!!');

    }
...
...

인앱 결제 로직 구현 코드

  • Expo의 InAppPurchases 라이브러리를 사용하여 인앱 결제 로직을 구현하기로 했지만, 구글 개발자 계정내에 등록된 인앱 결제 상품을 연결하는 절차까지만 성공하고 막상 결제는 계속해서 실패하였습니다. API 문서를 다시 한 번 정독하고, 코드 흐름을 꼼꼼하게 살펴본 결과 상품을 구매하기 전에 필수적으로 불러야하는 콜백함수의 위치가 특정 화면 단이 아니라 전역 상태로 있어야 하는 사실을 깨달았습니다.

  • 위치를 옮기고나서, 구매 후의 결과 처리가 제대로 되지 않으면 향후 구매가 취소될 수 있기 때문에 이에 대한 코드를 다시 한 번 확실히 검토하였습니다. 결제 형태가 구독인지, 아니면 일회성 상품인지에 따라서도 다른 처리를 해줘야하기 때문에, 이를 잘 고려하여 최종적으로 기능 구현을 완료하였습니다.

  • 인앱 상품 불러오기 부터 어떤 상품인지에 대한 확인 및 결제 후 처리까지 구현해보면서 인앱 결제의 전체적인 프로세스 잘 이해할 수 있었고, 향후 결제 관련 API 들을 사용할 때에도 이 경험을 잘 살려서 적용해볼 예정입니다.

구현 핵심 기능 2 : 플레이 스토어 배포를 위한 빌드 문제 해결

  • 플레이 스토어에 등록 가능한 안드로이드 앱 형태로 빌드하기 위해 Expo EAS Build 를 통해 프로젝트를 빌드하려 했지만, 다음과 같은 문제가 발생하였습니다.

image
Expo EAS Build Error

  • Plugin with id 'maven' not found. : maven plugin이 제거되어, maven-publish 플러그인을 대신 사용하도록 코드를 수정하였습니다. -> apply plugin: maven-publish 코드를 적용

  • compiledSdkVersion is not specified : build.gradlebuildscript 블럭 내의 코드를 수정하였습니다.

...
...
android {
    ndkVersion rootProject.ext.ndkVersion

    compileSdkVersion rootProject.ext.compileSdkVersion

    defaultConfig {
        applicationId 'com.candykorean.candykoreanapp'
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 42
        versionName "1.0.3"
        buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
        // missingDimensionStrategy "store", "play"

        if (isNewArchitectureEnabled()) {
            // We configure the CMake build only if you decide to opt-in for the New Architecture.
            externalNativeBuild {
                cmake {
                    arguments "-DPROJECT_BUILD_DIR=$buildDir",
                        "-DREACT_ANDROID_DIR=${reactNativeRoot}/ReactAndroid",
                        "-DREACT_ANDROID_BUILD_DIR=${reactNativeRoot}/ReactAndroid/build",
                        "-DNODE_MODULES_DIR=$rootDir/../node_modules",
                        "-DANDROID_STL=c++_shared"
                }
            }
            if (!enableSeparateBuildPerCPUArchitecture) {
                ndk {
                    abiFilters (*reactNativeArchitectures())
                }
            }
        }
    }
...
...

build.gradle -> android 코드 블럭 내 compiledSdkVersion 추가

Google Play Store URL

About

외국인들을 위한 한국어 학습 플랫폼

Resources

Stars

1 star

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages