일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 안드로이드 sunflower
- 막내의막무가내 목표 및 회고
- 부스트코스에이스
- 안드로이드
- 막내의막무가내 일상
- Fragment
- 막내의막무가내 알고리즘
- 막내의막무가내 안드로이드 코틀린
- 주엽역 생활맥주
- 2022년 6월 일상
- 막무가내
- 막내의막무가내 프로그래밍
- 프래그먼트
- 막내의막무가내 코틀린 안드로이드
- 막내의막무가내 SQL
- 막내의막무가내 플러터 flutter
- 막내의막무가내
- 막내의막무가내 코볼 COBOL
- 막내의막무가내 rxjava
- flutter network call
- 주택가 잠실새내
- 막내의막무가내 안드로이드
- 부스트코스
- 막내의막무가내 안드로이드 에러 해결
- 프로그래머스 알고리즘
- 막내의 막무가내
- 막내의막무가내 플러터
- 막내의막무가내 코틀린
- 안드로이드 Sunflower 스터디
- 막내의 막무가내 알고리즘
- Today
- Total
막내의 막무가내 프로그래밍 & 일상
[AWS] 아마존웹서비스 안드로이드, ec2, mysql, nodejs, 탄력적 ip (예약관리 앱) 본문
[2021-04-30 업데이트]
aws, mysql, android, nodejs 를 사용해서 간단한 어플리케이션 만들어보기
AWS를 사용해보고 싶어서 안드로이드와 접목하면서 했던것들을 기록할려고 합니다.
$$주의$$ : 나중에 다시 AWS를 사용하게 된다면 기억을 떠올리고자 부분부분을 간단한 메모형식으로 작성하는글이라 보기 힘들 수 있습니다.
aws 가입
ec2, rds 인스턴스를 생성해야하는데 유로 상품을 쓸거아니면 모두 프리티어로 하자. 1년간 꽁자다. ㅎㅎ
난 EC2에서 리눅스로 생성하였다. 그 후는 프리티어로 그냥 다 설정해주면 된다. (밑 사이트를 참고하면된다.. 아주 자세히작성하셨습니다.!! RDS의 경우에도 마찬가지이다. mysql 프리티어 선택)
이거 외에도 보안그룹이라든가 키페어 생성등도 잘 작성되어있다. (난 aws키는 aws폴더를 따로만들어 잘 보관중이다.)
putty를 통해 리눅스서버에 원격으로 접속이 가능하다.
PUTTY는 SSH를 사용하기 때문에 리눅스 서버에 접속하기 위해 이전 글에서 만들었던 개인키 파일이 필요하다. 이거에 대해서도 다음 블로그에서 자세히 설명이 되어있습니다.
https://blog.naver.com/zion830/221353353266
밑 글을 참고하여 RDS인스턴스를 생성한다.
https://blog.naver.com/zion830/221354722615
참고로 나는 포트번호를 3308로 하고 mysql버전을 8로하였다. 과금안되게 프리티어로 잘 설정하도록하자. 그리고 디비이름과 사용자이름 암호 다 나중에 접속하는데 사용하므로 잘 설정하도록 하자.
여기서 연결&보안에 퍼블릭 엑세스 가능성을 '예'로 해놔야하는다. (내컴터같은 다른곳에서 aws컴터로 접근할려면) 이건 처음 rds생성할때 옵션이 안보여서 RDS생성 후 위에 사진의 수정버튼을 통해 수정해주었다.
RDS를 생성하고 mysql workbench로 테이블을 생성하고 관리하자. (cmd창으로도 할수있지만 매우 불편하므로)
이것도 밑 블로그에 자세히 작성되어있습니다. 주의해야할 점은 username과 암호를 내가 RDS인스턴스를 생성할때에 작성한거와 맞게 작성해주어야한다. 또한 호스트네임은 당연히 ec2가 아닌 rds 엔드포인트를 작성해야한다.
마지막으로 생성 후 데이터베이스를 생성할 때 내가 RDS를 생성했을때의 스키마 이름으로 해야한다. (example)
밑은 내가 처음 RDS생성할떄 example로 했던 캡처사진이다.
데이터베이스 스키마 생성
USE example
https://blog.naver.com/zion830/221354730576
nodejs 세팅 및 작성하는법과 파일질라로 올리는건 다음을 참고하면된다.
https://blog.naver.com/zion830/221396511803
https://blog.naver.com/zion830/221407674520
파일질라에서 호스트는 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 변경 성공'
})
}
}
})
});
잘 통신이되고 디비에 값이 삽입되는것을 확인할 수 있었습니다. 이렇게 통신이 된걸 확인한 후에는 안드로이드 프로젝트를 실행하고 안드로이드에서 레트로핏2를 사용해서 통신해주면 됩니다.
만약 putty에서 node app.js 이렇게 실행을 할텐데
그럼 putty를 종료시키면 서버가 죽어서 post방식으로 요청을해도 응답이 오질 않습니다.
그래서 pm2를 설치해주고
pm2 명령어로 노드를 실행시켜주면 됩니다.
명령어 정리잘해논사이트
그럼 실행시키고 푸티를 꺼도 죽지않습니다.
인스턴스를 중지시키고 다시실행시키면 IP가 바뀐다. 그래서 탄력적 IP주소를 활용해서 고정아이피로 박아놓으면 좋다. 사용법은 아래사이트에서 볼 수 있다.
https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html
안드로이드 관련 코드 (레트로핏 사용, 디비삽입까지 잘 됨을 확인)
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
참고한 사이트 : (코알라일락님 블로그를 참고해서 포스팅을 할 수 있도록 허락 해주셔서 감사합니다.)
https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/create-deploy-nodejs.rds.html
https://blog.naver.com/zion830/221354730576
댓글, 공감, 구독은 큰 힘이 됩니다. 감사합니다. !!