관리 메뉴

막내의 막무가내 프로그래밍 & 일상

[AWS] 아마존웹서비스 안드로이드, ec2, mysql, nodejs, 탄력적 ip (예약관리 앱) 본문

AWS

[AWS] 아마존웹서비스 안드로이드, ec2, mysql, nodejs, 탄력적 ip (예약관리 앱)

막무가내막내 2019. 9. 13. 16:50
728x90

 

[2021-04-30 업데이트]

 

 

aws, mysql, android, nodejs 를 사용해서 간단한 어플리케이션 만들어보기

 

AWS를 사용해보고 싶어서 안드로이드와 접목하면서 했던것들을 기록할려고 합니다. 

$$주의$$ : 나중에 다시 AWS를 사용하게 된다면 기억을 떠올리고자 부분부분을 간단한 메모형식으로 작성하는글이라 보기 힘들 수 있습니다.

 

aws 가입 


ec2, rds 인스턴스를 생성해야하는데 유로 상품을 쓸거아니면 모두 프리티어로 하자. 1년간 꽁자다. ㅎㅎ 

난 EC2에서 리눅스로 생성하였다. 그 후는 프리티어로 그냥 다 설정해주면 된다. (밑 사이트를 참고하면된다.. 아주 자세히작성하셨습니다.!!  RDS의 경우에도 마찬가지이다. mysql 프리티어 선택)

이거 외에도 보안그룹이라든가 키페어 생성등도 잘 작성되어있다. (난 aws키는 aws폴더를 따로만들어 잘 보관중이다.)

https://blog.naver.com/PostView.nhn?blogId=zion830&logNo=221353335126&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView

 


putty를 통해 리눅스서버에 원격으로  접속이 가능하다.

PUTTY는 SSH를 사용하기 때문에 리눅스 서버에 접속하기 위해 이전 글에서 만들었던 개인키 파일이 필요하다. 이거에 대해서도 다음 블로그에서 자세히 설명이 되어있습니다.

https://blog.naver.com/zion830/221353353266


밑 글을 참고하여 RDS인스턴스를 생성한다. 

https://blog.naver.com/zion830/221354722615

 

AWS를 활용한 안드로이드 앱 (3) RDS 인스턴스 생성하기

이 글은 본인의 AWS 이용기를 정리한 글로, 글 전체의 목차는 https://blog.naver.com/zion830/22135330...

blog.naver.com

참고로 나는 포트번호를 3308로 하고 mysql버전을 8로하였다.  과금안되게 프리티어로 잘 설정하도록하자. 그리고 디비이름과 사용자이름 암호 다 나중에 접속하는데 사용하므로 잘 설정하도록 하자.

 

여기서 연결&보안에 퍼블릭 엑세스 가능성을 '예'로 해놔야하는다. (내컴터같은 다른곳에서 aws컴터로 접근할려면) 이건 처음 rds생성할때 옵션이 안보여서 RDS생성 후 위에 사진의 수정버튼을 통해 수정해주었다.

 


RDS를 생성하고 mysql workbench로 테이블을  생성하고 관리하자. (cmd창으로도 할수있지만 매우 불편하므로)

이것도 밑 블로그에 자세히 작성되어있습니다. 주의해야할 점은 username과 암호를 내가 RDS인스턴스를 생성할때에 작성한거와 맞게 작성해주어야한다. 또한 호스트네임은 당연히 ec2가 아닌 rds 엔드포인트를 작성해야한다.

마지막으로 생성 후 데이터베이스를 생성할 때 내가 RDS를 생성했을때의 스키마 이름으로 해야한다. (example)

밑은 내가 처음 RDS생성할떄 example로 했던 캡처사진이다.

아니다 지금보니깐 나도 3306으로했다. ......

데이터베이스 스키마 생성

USE example

https://blog.naver.com/zion830/221354730576

 

AWS를 활용한 안드로이드 앱 (4) RDS와 MySQL WorkBench 연결 & 회원정보 테이블 생성하기

이 글은 본인의 AWS 이용기를 정리한 글로, 글 전체 목차는 https://blog.naver.com/zion830/2213533063...

blog.naver.com


 

 

 

 

nodejs 세팅 및 작성하는법과 파일질라로 올리는건 다음을 참고하면된다.

https://blog.naver.com/zion830/221396511803

 

AWS를 활용한 안드로이드 앱 (5) node.js 설치&로그인, 회원가입 코드 작성

이 글은 본인의 AWS 이용기를 정리한 글로, 글 전체의 목차는 https://blog.naver.com/zion830/22135330...

blog.naver.com

https://blog.naver.com/zion830/221407674520

 

[AWS] FileZila로 EC2 인스턴스에 파일 업로드하기

이전 포스팅에선 vim으로 터미널에서 직접 코드를 편집하는 방법을 소개했다. vim도 나름 편집에 도움을 주...

blog.naver.com

파일질라에서 호스트는 ec2 엔드포인트를 작성해주면 된다. (퍼블렉DNS)

 

웹스톰으로 편하게 코드를 작성 후 깃헙에 올려서 putty를 통해 aws에서 클론하거나

아니면 코드작성한 프로젝트를 파일질라를 통해 aws로 옮겨주면된다.

그리고 참고한 블로그에서의 코드가 저한테 안먹어서 바디파서를 추가하고 다음과 같이 살짝 바꿧습니다. 또한 listen에 가운데에 매개변수에 localhost가 원래있었는데 해당부분을 없에주었습니다.

 

그리고 간단하게 만든거라 따로 컨트롤러로 안나누고 app.js에 다 처리하게 하였습니다.

var mysql = require('mysql');
var express = require('express');
var bodyParser = require('body-parser');
var app = express();

app.use(bodyParser.json({extended: true})); //사용자가 웹사이트로 전달하는 정보들을 검사하는 미들웨어
app.use(bodyParser.urlencoded({extended: true})); //json이 아닌 post형식으로올때 파서

app.listen(3000, function () {
    console.log('서버 실행');
});

//디비연결
var connection = mysql.createConnection({
    host: "dmazonaws.com",
    user: "w",
    database: "e",
    password: "t",
    port: 3306
});

//회원가입
app.post('/user/join', function (req, res) {
    var userId = req.body.userId;
    var userPassword = req.body.userPassword;
    var userRestaurant = req.body.userRestaurant;
    var restaurantLocation = req.body.restaurantLocation;
    var restaurantTel = req.body.restaurantTel;
    var fcm = req.body.fcm;

    var sql = 'INSERT INTO Users (userId, userPassword, userRestaurant, restaurantLocation, restaurantTel, fcm ) VALUES (?, ?, ?, ?, ?, ?)';
    var params = [userId, userPassword, userRestaurant, restaurantLocation, restaurantTel, fcm];

    connection.query(sql, params, function (err, result) {
        if (err)
            console.log(err);
        else {
            res.json({
                result: true,
                msg: '회원가입에 성공했습니다.'
            })
        }
    });
});

//아이디 중복확인
app.post('/user/checkId', function (req, res) {
    var userId = req.body.userId;
    var sql = 'select * from Users where userId = ?';
    connection.query(sql, userId, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (result.length === 0) {
                res.json({
                    result: false,
                    msg: '사용가능한 아이디입니다.'
                });
            } else {
                res.json({
                    result: true,
                    msg: '중복된 아이디가 존재합니다'
                });
            }

        }
    })
});


//로그인
app.post('/user/login', function (req, res) {
    var userId = req.body.userId;
    var userPassword = req.body.userPassword;
    var sql = 'select * from Users where userId = ? AND userPassword = ?';
    var params = [userId, userPassword];
    connection.query(sql, params, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (result.length === 0) {
                res.json({
                    result: false,
                    msg: '존재하지 않는 계정입니다!'
                });
            } else if (userPassword !== result[0].userPassword) {
                res.json({
                    result: false,
                    msg: '비밀번호가 틀렸습니다!'
                });
            } else {
                res.json({
                    result: true,
                    msg: '로그인 성공!',
                    id : result[0].id,
                    userId: userId,
                    userPassword: result[0].userPassword,
                    userRestaurant: result[0].userRestaurant,
                    restaurantLocation : result[0].restaurantLocation,
                    restaurantTel : result[0].restaurantTel
                });
            }

        }
    })
});


//레스토랑검색
app.post('/restaurant/search', function (req, res) {
    var userRestaurant = req.body.userRestaurant;
    var sql = "select * from Users where userRestaurant LIKE " +connection.escape('%'+req.body.userRestaurant+'%');

    connection.query(sql, userRestaurant, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (result.length === 0) {

            } else {
                res.json({
                    result
                });
            }
        }
    })
});


//레스토랑 예약
app.post('/restaurant/insert', function (req, res) {
    var restaurant_id = req.body.restaurant_id;
    var restaurant_name = req.body.restaurant_name;
    var apply_id = req.body.apply_id;
    var apply_date = req.body.apply_date;
    var reserve_date = req.body.reserve_date;
    var user_tel = req.body.user_tel;
    var user_pw = req.body.user_pw;
    var accept = req.body.accept;
    var fcm = req.body.fcm;

    var sql = 'INSERT INTO Apply (restaurant_id, restaurant_name, apply_id, apply_date, reserve_date, user_tel,user_pw, accept, fcm ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)';
    var params = [restaurant_id, restaurant_name, apply_id, apply_date, reserve_date, user_tel,user_pw, accept, fcm];

    connection.query(sql, params, function (err, result) {
        if (err)
            console.log(err);
        else {
            res.json({
                result: true,
                msg: '예약이 성공했습니다'
            })
        }
    });
});

//특정 레스토랑 전체 예약리스트(전체날짜)
app.post('/restaurant/admin', function (req, res) {
    var restaurant_id = req.body.restaurant_id;
    console.log(restaurant_id, typeof(restaurant_id));
    var sql = "select * from Apply where restaurant_id = ? ORDER BY reserve_date";
    connection.query(sql, restaurant_id, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (result.length === 0) {

            } else {
                res.json({
                    result
                });
            }
        }
    })
});

//특정 레스토랑  예약리스트(특정날짜)
app.post('/restaurant/admin/certain', function (req, res) {
    var restaurant_id = req.body.restaurant_id;
    var reserve_date = req.body.reserve_date;
    var sql = "select * from Apply where restaurant_id = ? AND reserve_date LIKE " + connection.escape('%'+req.body.reserve_date+'%') + "order by reserve_date";
    var params = [restaurant_id, reserve_date];
    connection.query(sql, params, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (result.length === 0) {

            } else {
                res.json({
                    result
                });
            }
        }
    })
});

//예약 수락
app.post('/restaurant/accept', function (req, res) {
    var id = req.body.id;
    var sql = "update Apply set accept = 'yes' where id = ?";

    connection.query(sql, id, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (err)
                console.log(err);
            else {
                res.json({
                    result: true,
                    msg: '예약이 수락되었습니다.'
                })
            }
        }
    })
});

//예약 수락, 거절 알림
app.post('/restaurant/accept/alarm', function (req, res) {
    var id = req.body.id;
    var sql = "select * from Apply where id = ?";

    connection.query(sql, id, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (err)
                console.log(err);
            else {
                res.json({
                    result: true,
                    fcm: result[0].fcm,
                    restaurant_name: result[0].restaurant_name
                })
            }
        }
    })
});

//예약신청 했다고 상대방에게 알림
app.post('/restaurant/reserve/alarm', function (req, res) {
    var id = req.body.id;
    var sql = "select * from Users where id = ?";

    connection.query(sql, id, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (err)
                console.log(err);
            else {
                res.json({
                    result: true,
                    fcm: result[0].fcm,
                    userId: result[0].userId
                })
            }
        }
    })
});

//예약수락한거 취소
app.post('/restaurant/cancel', function (req, res) {
    var id = req.body.id;
    var sql = "update Apply set accept = 'no' where id = ?";

    connection.query(sql, id, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (err)
                console.log(err);
            else {
                res.json({
                    result: true,
                    msg: '예약이 취소되었습니다.'
                })
            }
        }
    })
});

//내가 예약한 레스토랑
app.post('/user/reserve', function (req, res) {
    var apply_id = req.body.apply_id;
    var user_tel = req.body.user_tel;
    var user_pw = req.body.user_pw;
    console.log("내가 예약한 가게검색");
    var sql = "select * from Apply where apply_id = ? AND user_tel = ? AND  user_pw = ? order by reserve_date";
    var params = [apply_id, user_tel, user_pw];
    connection.query(sql, params, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (result.length === 0) {

            } else {
                res.json({
                    result
                });
            }
        }
    })
});

//음식점(유저) fcm 변경
app.post('/user/fcm', function (req, res) {
    var id = req.body.id;
    var fcm = req.body.fcm;
    var sql = "update Users set fcm = ? where id = ?";
    var params = [id, fcm];
    connection.query(sql, params, function (err, result) {
        if (err)
            console.log(err);
        else {
            if (err)
                console.log(err);
            else {
                res.json({
                    result: true,
                    msg: 'FCM 변경 성공'
                })
            }
        }
    })
});

 

ec2 주소 유출위험성 때문에 잘라서 붙였습니다.(포스트맨)
포트는 3000으로 했으므로 ec2주소 뒤에 3000포트를 작성해준다.

 

처음 포스트맨으로 테스트한 결과입니다. 그 이후 변수명(컬럼명)을 변경하였습니다.
안붙여도 되긴한다.

 

잘 통신이되고 디비에 값이 삽입되는것을 확인할 수 있었습니다. 이렇게 통신이 된걸 확인한 후에는 안드로이드 프로젝트를 실행하고 안드로이드에서 레트로핏2를 사용해서 통신해주면 됩니다.

 

 

 


 

 

 

만약 putty에서 node app.js 이렇게 실행을 할텐데

그럼 putty를 종료시키면 서버가 죽어서 post방식으로 요청을해도 응답이 오질 않습니다.

그래서 pm2를 설치해주고

pm2 명령어로 노드를 실행시켜주면 됩니다.

http://pm2.keymetrics.io/

 

PM2 ·

Advanced process manager for production Node.js applications. Load balancer, logs facility, startup script, micro service management, at a glance.

pm2.keymetrics.io

명령어 정리잘해논사이트

https://massivcode.com/5

 

그럼 실행시키고 푸티를 꺼도 죽지않습니다.


인스턴스를 중지시키고 다시실행시키면 IP가 바뀐다. 그래서 탄력적 IP주소를 활용해서 고정아이피로 박아놓으면 좋다. 사용법은 아래사이트에서 볼 수 있다.

 

https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html

 

탄력적 IP 주소 - Amazon Elastic Compute Cloud

탄력적 IP 주소 탄력적 IP 주소는 동적 클라우드 컴퓨팅을 위해 고안된 정적 IPv4 주소입니다. 탄력적 IP 주소는 AWS 계정과 연결됩니다. 탄력적 IP 주소를 사용하면 주소를 계정의 다른 인스턴스에 신속하게 다시 매핑하여 인스턴스나 소프트웨어의 오류를 마스킹할 수 있습니다. 탄력적 IP 주소는 인터넷에서 연결 가능한 퍼블릭 IPv4 주소입니다. 인스턴스에 퍼블릭 IPv4 주소가 없는 경우 탄력적 IP 주소를 인스턴스와 연결하여 인터넷과 통신을 활성

docs.aws.amazon.com

 

 

 


 

 

 

 

안드로이드 관련 코드 (레트로핏 사용, 디비삽입까지 잘 됨을 확인)

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class ApiClient {
    private static final String BASE_URL = "ec2주소:3000/";
    private static Retrofit retrofit;

    public static Retrofit getApiClient(){
        if(retrofit == null){
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return  retrofit;
    }
}
import com.mtjin.aws_number_ticket.api.ApiClient;
import com.mtjin.aws_number_ticket.api.ApiInterface;
import com.mtjin.aws_number_ticket.model.User;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class SignupPresenter  implements SignupContract.Presenter{

    private SignupContract.View view;

    public SignupPresenter(SignupContract.View view) {
        this.view = view;
    }


    @Override
    public void requestCheckId(String userId) {
        view.showProgress();
        ApiInterface apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
        Call<User> call = apiInterface.checkUser(userId);

        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                view.hideProgress();
                if(response.isSuccessful() && response.body() != null){
                    Boolean isDuplicate = response.body().getResult(); //중복된아이디인지
                    if(isDuplicate){ //중복
                        view.onToastMessage("중복된 아이디가 이미 존재합니다.");
                    }else{
                        view.onToastMessage("사용가능한 아이디입니다.");
                        view.blockUserId();
                    }
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                view.hideProgress();
                view.onToastMessage(t.getLocalizedMessage());
            }
        });
    }

    @Override
    public void requestSignup(String userId, String userPassword, String userRestaurant) {
        view.showProgress();
        ApiInterface apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
        Call<User> call = apiInterface.saveUser(userId, userPassword, userRestaurant);
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                view.hideProgress();
                if(response.isSuccessful() && response.body() != null){
                    Boolean isSuccess = response.body().getResult();
                    if(isSuccess){
                        view.onToastMessage("회원가입 되었습니다.");
                    }
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                view.hideProgress();
                view.onToastMessage(t.getLocalizedMessage());
            }
        });
    }
}
import android.content.Context;


public interface SignupContract {
    interface View {
        void onToastMessage(String message); //토스트메세지
        void showProgress();
        void hideProgress();
        void blockUserId(); //중복확인 후 아이디 변경 못하게막기
    }
    interface Presenter{
        void requestCheckId(String userId); //아이디 중복확인 요청
        void requestSignup(String userId, String userPassword, String userRestaurant, String restaurantLocation , String restaurantTel, String fcm); //회원가입 요청
    }
}
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.mtjin.aws_number_ticket.R;
import com.mtjin.aws_number_ticket.activity.login.LoginActivity;

public class SignupActivity extends AppCompatActivity implements SignupContract.View{
    EditText idEdit;
    EditText pwEdit;
    EditText pwConfirmEdit;
    EditText restaurantEdit;
    Button duplicateIdCheckEdit;
    Button okButton;
    ProgressDialog progressDialog;
    //value
    SignupPresenter presenter;
    boolean isChecked = false; //아이디 중복체크여부
    Button.OnClickListener onClickListener;
    String userId;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_signup);
        idEdit = findViewById(R.id.signup_et_id);
        pwEdit = findViewById(R.id.signup_et_pw);
        pwConfirmEdit = findViewById(R.id.signup_et_pwconfrim);
        restaurantEdit = findViewById(R.id.signup_et_restaurant);
        duplicateIdCheckEdit = findViewById(R.id.signup_btn_check);
        okButton = findViewById(R.id.signup_btn_ok);
        setOnClickListener();
        duplicateIdCheckEdit.setOnClickListener(onClickListener);
        okButton.setOnClickListener(onClickListener);

        //다이얼로그
        progressDialog = new ProgressDialog(this);
        progressDialog.setMessage("잠시만 기다려주세요 :)");

        //프레젠터
        presenter = new SignupPresenter(this);
    }

    private void setOnClickListener(){
        onClickListener = new Button.OnClickListener() {
            @Override
            public void onClick(View view) {
                switch (view.getId()){
                    case R.id.signup_btn_check:
                         userId = idEdit.getText().toString().trim();
                        Log.d("FFFF", userId);
                        if(userId.length() <= 0){
                           idEdit.setError("아이디를 입력해주세요");
                        }else{
                            presenter.requestCheckId(userId);
                        }
                        break;
                    case R.id.signup_btn_ok:
                        String pwd = pwEdit.getText().toString().trim();
                        String pwd2 = pwConfirmEdit.getText().toString().trim();
                        String restaurant = restaurantEdit.getText().toString().trim();
                        if(!isChecked){
                            onToastMessage("아이디 중복체크를 먼저 해주세요.");
                        }else if(pwd.length() <= 0){
                            onToastMessage("비밀번호를 작성해주세요.");
                        }else if(!pwd.equals(pwd2)){
                            onToastMessage("비밀번호가 서로 다릅니다.");
                        }else if(restaurant.length() <= 0){
                            onToastMessage("음식점 이름 작성해주세요.");
                        } else{
                            presenter.requestSignup(userId, pwd, restaurant);
                            finish();
                        }
                        break;
                }
            }
        };
    }

    @Override
    public void onToastMessage(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showProgress() {
        progressDialog.show();
    }

    @Override
    public void hideProgress() {
        progressDialog.hide();
    }

    @Override
    public void blockUserId() {
        idEdit.setFocusable(false);
        idEdit.setEnabled(false);
        duplicateIdCheckEdit.setEnabled(false);
        isChecked = true;
    }
}
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class User {
    @Expose
    @SerializedName("userId") private int userId;
    @Expose
    @SerializedName("userPassword") private String userPassword;
    @Expose
    @SerializedName("userRestaurant") private String userRestaurant;
    @Expose
    @SerializedName("result") private Boolean result;
    @Expose
    @SerializedName("msg") private String msg;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserPassword() {
        return userPassword;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }

    public String getUserRestaurant() {
        return userRestaurant;
    }

    public void setUserRestaurant(String userRestaurant) {
        this.userRestaurant = userRestaurant;
    }

    public Boolean getResult() {
        return result;
    }

    public void setResult(Boolean result) {
        this.result = result;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

 

 

 

코드 저장소입니다.

github.com/mtjin/aws_number_ticket

 

mtjin/aws_number_ticket

aws 이용한 간단한 안드로이드 토이프로젝트. Contribute to mtjin/aws_number_ticket development by creating an account on GitHub.

github.com

 

 

 


참고한 사이트 : (코알라일락님 블로그를 참고해서 포스팅을 할 수 있도록 허락 해주셔서 감사합니다.)

https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/create-deploy-nodejs.rds.html 

 

Amazon RDS DB 인스턴스를 Node.js 애플리케이션 환경에 추가 - AWS Elastic Beanstalk

Amazon RDS DB 인스턴스를 Node.js 애플리케이션 환경에 추가 Amazon Relational Database Service(Amazon RDS) DB 인스턴스를 이용해 애플리케이션이 수집하고 수정한 데이터를 저장할 수 있습니다. 데이터베이스를 환경에 연결하고 Elastic Beanstalk에서 관리하도록 하거나 외부에서 만들고 관리할 수 있습니다. Amazon RDS를 처음 사용하는 경우 Elastic Beanstalk 관리 콘솔로 테스트

docs.aws.amazon.com

https://blog.naver.com/zion830/221354730576

 

AWS를 활용한 안드로이드 앱 (4) RDS와 MySQL WorkBench 연결 & 회원정보 테이블 생성하기

이 글은 본인의 AWS 이용기를 정리한 글로, 글 전체 목차는 https://blog.naver.com/zion830/2213533063...

blog.naver.com

 

 

댓글, 공감, 구독은 큰 힘이 됩니다. 감사합니다. !!

728x90
Comments