삽질도사

[안드로이드] nodejs로 firebase hosting 꿀팁대방출(앱으로 사업자번호 조회하기 위해서) 본문

안드로이드

[안드로이드] nodejs로 firebase hosting 꿀팁대방출(앱으로 사업자번호 조회하기 위해서)

삽질도사 2021. 11. 20. 00:19
반응형

nodejs에서 express()를 사용하여 웹 서버를 파고, 퍼블릭하게 사용자가 사용할 수 있도록 하기 위한 방법입니다.

쉽거나 일반적으로 호스팅하는 방법은 다른 블로그에 80%정도 올라와있으니 하나도 모르시면 다른 블로그 보시고

언젠가 겪을 에러에 대비 하실 거라면 이 게시글을 잘 마크해놨다가 보십시오.

 

일어나는 오류가 다방면으로 일어나기 일쑤이기이고 stackoverflow에 가도 찾기힘든, 빼먹을만한 에러사항만 검토하겠습니다. 

 

1. module 받을 때.

 

firebase hosting으로 모듈 받을 때에 npm install (모듈이름)을 프로젝트 루트에서 받지 마시고, cd functions를 입력 후 npm install (모듈이름)을 적어서 그곳에 모듈을 설치하셔야 합니다. (거기에 node_modules가 있음)

 

2. "npm --prefix \"%RESOURCE_DIR%\" run lint" 과 "rewrites":[{ "source": "**", "function": "api" }] 를 firebase.json에 집어넣자

{
  "functions": {
    "predeploy": [
      "npm --prefix \"%RESOURCE_DIR%\" run lint"
    ]
  },
  "hosting": {
    "public": "pulic",
 
    "rewrites":[{ "source": "**", "function": "api" }]
  }
}

경로문제 때문에 원래 적혀있던 "npm --prefix \"RESOURCE_DIR\" run lint" <- 얘는 정상작동을 안하기 때문에 deploy(코드를 적용)하면 에러가 발생한다. 

저기 두 번째에 rewrites: 어쩌고 저쩌고는 기존에 firebase에 웹을 커스텀해서 적용시키려면 구글의 방식을 따라야하는데 그거 싫고 "express()를 써서 내가 하고싶은대로 쓸거다~" 라고 한다면 "function": "api" 를 적어줌으로써 api라는 함수를 넘길테니 이거대로 해달라고 firebase에 주문서를 넣는다고 생각하면 된다(사실 나도 잘은 모르지만 어쨋든 해야함)  그럼 index.js(우리가 적용시킬 소스코드 main)을 보자 

const express = require("express");
const functions = require("firebase-functions");
// const path = require('path')
const axios = require("axios"); // http 모듈
const xml2js = require("xml2js"); // xml 파싱 모듈
const app = express();
// const PORT = process.env.PORT || 3000;

// var http = require('http');
// var bodyParser = require('body-parser');

// 국세청 사업자번호 조회 API [POST]
const postUrl = "https://teht.hometax.go.kr/wqAction.do?actionId=ATTABZAA001R08&screenId=UTEABAAA13&popupYn=false&realScreenId=";

// API 에 raw 로 올라갈 xml 데이터
const xmlRaw = "<map id=\"ATTABZAA001R08\"><pubcUserNo/><mobYn>N</mobYn><inqrTrgtClCd>1</inqrTrgtClCd><txprDscmNo>{CRN}</txprDscmNo><dongCode>15</dongCode><psbSearch>Y</psbSearch><map id=\"userReqInfoVO\"/></map>";

app.get("/", (req, res)=>res.send("hello"));
// app.listen(PORT, ()=>console.log("asdads"));

app.post("/", (req, res) => {
    // "3051577349"
    const CRNumber = req.body.id;
    // req.body.id

    if (CRNumber != null) {
        postCRN(CRNumber)
            .catch((err) => console.log(err))
            .then((result) => {
                const app = {"approve_id": result};
                console.log(app);
                res.send(app);
        });
    }
});

//밑에 보이는 exports.api (<- 이놈이 rewrite에 function:api라고 적은 그놈)로 firebase에 서식을 넘겨준다
exports.api = functions.https.onRequest(app);

function postCRN(crn) {
    return new Promise((resolve, reject) => {
        axios.post(postUrl, xmlRaw.replace(/\{CRN\}/, crn), {headers: {"Content-Type": "text/xml"}})
            .catch((err) => reject(err))
            .then((result) => {
                getCRNresultFromXml(result["data"]).catch((err) => reject(err)).then((result) => resolve(result));
            });
    });
}

function getCRNresultFromXml(dataString) {
    return new Promise((resolve, reject) => {
        xml2js.parseString(dataString,
            (err, res) => {
                if (err) reject(err);
                else resolve(res.map.trtCntn[0]);
            });
    });
}

복잡해보이지만 위에 exports.api = functions.https.onRequest(app); 로 app이라는 변수를 넘겨줬는데 이게 바로 express()를 쓴 변수라는 것을 알 수있다. (한 마디로 적을거 다 적고 저것만 복붙하면댐)

 

3. functions predeploy error: Command terminated with non-zero exit code1 에러 

위에 잘 보면 38라인에 세미콜론 빠졌다고 혼내는 게 보임

삽질을 하면 할수록 미궁으로 빠지는 에러코드이고 이유가 다양하지만 일반적으로 그냥 문법에러임.

eslint라는 문법을 준수하여 코드를 작성해야하는데 중딩때 학주마냥 엄청나게 까탈스러워서 띄어쓰기만 하나 어긋나도 에러가 되어버리니까 에러코드 보면서 하나하나 고쳐쓰던지 eslint 파일이 있으니까 들어가서 무시하라는 코드를 넣던지 해야합니다.

eslintrc.js 파일에 들어가서 rules를 좀 건드리면 됩니다.

본인 에러코드보고 판단해서 구글링하고 rules 어떻게 건들이는 지 검색 조금만하면 나옵니다.

위에 보이는 코드는 한 줄에 적히는 최대 글자수를 제한하는 max-len을 500자로 손봐주었고,

Missing JSDoc comment 라면서 함수마다 코멘트 달으라고 하는(?) 것도 차단해버린 것을 알 수 있습니다.

 

복붙해야 되니까 밑에 사진말고 코드 똑같은거 적어놓겠습니다.

module.exports = {
  root: true,
  env: {
    es6: true,
    node: true,
  },
  extends: [
    "eslint:recommended",
    "google",
  ],
  rules: {
    "quotes": ["error", "double"],
    "indent": "off",
    "max-len": ["error", 500, 2, {
      "ignoreUrls": true,
      "ignoreComments": false,
      "ignoreRegExpLiterals": true,
      "ignoreStrings": false,
      "ignoreTemplateLiterals": false,
    }],

    "require-jsdoc": ["error", {
      "require": {
          "FunctionDeclaration": false,
          "MethodDefinition": false,
          "ClassDeclaration": false,
          "ArrowFunctionExpression": false,
          "FunctionExpression": false,
      },
  }],
  },
};

복붙굿

 

4. index.html 지우기

nodejs로 웹페이지 커스텀해서 쓸거면 프로젝트파일 내부에 index.html 지우세요.

공식문서에 개미똥자루만하게 "firebase hosing은 index.html을 기본경로로 지정하고 없을 시에 다음 경로를 찾는다"라고 적혀있으니 저게 없어야 index.js로 우리가 만든 커스텀을 적용시킬 수 있습니다. (아닐수도 있음 근데 해도 문제안됌)

 

5. hosting 나만 볼거 아니면 permission을 allUsers를 추가해야 hosting이 적용됩니다. (젤 중요)

https://cloud.google.com/functions/docs/securing/managing-access-iam

 

IAM을 통한 액세스 승인  |  Cloud Functions 문서  |  Google Cloud

의견 보내기 IAM을 통한 액세스 승인 ID 및 액세스 관리(IAM)를 사용하여 ID 생성, 업데이트, 삭제와 같이 함수에 대한 관리 작업을 수행할 권한을 ID에 부여합니다. 함수에 주 구성원(사용하려는 ID,

cloud.google.com

다른 거 보지말고 스크롤 쭉쭉내려서 

이거 보고 따라하면 됌

또 생각나면 추가하겠습니다~~~~~~ 다른 에러있으신 분은 댓글주시면 도와드리겠습니다.

 

자 이제 본론으로 넘어가서 위에 있는 난관을 모두 물리치고 웹에 nodejs 코드를 만족스럽게 올려서 모두가 사용할 수 있게 만들었다면 앱에서 정보를 받아올 수 있도록 코드를 작성할 것입니다.

    public void request(String str){

        //JSON형식으로 데이터 통신
        String url = "님들의 hosting 도메인 주소를 쓰세요";
        JSONObject testjson = new JSONObject();

        try {
            //입력해둔 edittext의 id와 pw값을 받아와 put해줍니다 : 데이터를 json형식으로 바꿔 넣어주었습니다.
            testjson.put("id",str);
            String jsonString = testjson.toString(); //완성된 json 포맷

            //이제 전송
            final RequestQueue requestQueue = Volley.newRequestQueue(SignActivity.this);
            final JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, url,testjson, new Response.Listener<JSONObject>() {

                //데이터 전달을 끝내고 이제 그 응답을 받을 차례입니다.
                @Override
                public void onResponse(JSONObject response) {
                    try {
                        //받은 json형식의 응답을 받아
                        JSONObject jsonObject = new JSONObject(response.toString());
                        String res = jsonObject.getString("approve_id");

                        if(res != null){

                            if (BN_CheckString(res)) {
                                businessNumCheck = true;
                                binding.businessNum.setEnabled(false); //사업자번호가 제대로 등록됬다면 수정불가능하게 만듬.
                                BUSINESSNUMBER = str;
                                loader.setVisibility(View.GONE);
                                ANSWER[0] = res;
                                Tost(res);
                            }else{
                                businessNumCheck = false;
                                loader.setVisibility(View.GONE);
                                Tost(res);
                            }

                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                //서버로 데이터 전달 및 응답 받기에 실패한 경우 아래 코드가 실행됩니다.
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    error.printStackTrace();
                    //Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_SHORT).show();
                }
            });
            jsonObjectRequest.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
            requestQueue.add(jsonObjectRequest);
            //
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

위 처럼 Volley로 requestqueue에 넣어서 정보를 response에서 받아 처리하면 됩니다! 

아까 nodejs에서 app.post함수에서 approve_id라는 key값으로 정보를 넘겨준 것을 여기 response에서 받아온 것이지요.

 

그 결과 이렇게 저는 사용했습니다.

 

반응형