Per ardua ad astra !
I'm On My Way
Per ardua ad astra !
전체 방문자
오늘
어제
  • 분류 전체보기 (126)
    • Algorithm (50)
      • 백준 (30)
      • SWEA (3)
      • JUNGOL (3)
      • Programmers (5)
      • LeetCode (2)
    • 안드로이드 개발 (6)
      • Java로 개발 (0)
      • Kotlin으로 개발 (3)
    • Spring (41)
      • Spring기본 (17)
      • JPA기본 (15)
      • JPA활용 SpringBoot 기본 (9)
      • API 개발 기본 (0)
    • 네트워크 (3)
    • 운영체제 (0)
    • Life (3)
      • 책 (0)
      • 자기계발 (1)
      • 일상 (2)
    • 노마드코더 (3)
      • python으로 웹 스크래퍼 만들기 (3)
    • 프로그래밍 언어 (17)
      • Java 기본 (2)
      • 코틀린 기본 (15)

블로그 메뉴

  • 홈
  • 방명록

인기 글

hELLO · Designed By 정상우.
Per ardua ad astra !

I'm On My Way

안드로이드 개발/Kotlin으로 개발

안드로이드 Volley와 웹 서버를 통한 DB 연결 (로그인, 회원 가입 기능 구현)

2021. 10. 29. 15:58

1. 프로잭트 생성, 환경 설정


의존 설정

build.gradle에서 dependencies에 의존을 추가한다.

implementation 'com.android.volley:volley:1.1.1'

 

그리고 뷰 컴포넌트의 직접적인 접근과 @Pacelize를 사용하기 위해 plugins에 코틀린 안드로이드 익스텐션을 추가한다.

plugins {
	...
    id 'kotlin-android-extensions'
}


더 많은 volley에 대한 정보: https://developer.android.com/training/volley?hl=ko

프로젝트의 기본 구성

프로젝트 디렉터리 구조


Activity  

MainActivity - 로그인 성공 후 사용자 정보를 보여줌
LoginActivity - 로그인 화면을 보여줌
RegisterActivity - 회원가입 화면을 보여줌 

Service

LoginService - 입력받은 userID, userPassword로 StringRequest을 만들어서 PHP를 통해 DB에 사용자가 있는지 확인하고 로그인 시켜주는 서비스 
RegisterService - 입력받은 사용자 정보로 StringRequest를 만들어서 PHP를 통해 DB에 회원정보를 추가하여 회원가입을 진행하는 서비스

Class

Member{
   userID
   userPassword
   userName
   userAge
}
사용자의 정보를 담은 Parcelize된 클래스 

 

Manifest설정

AndroidManifest.xml

  • Mainfest.xml에서 시작화면을 LoginActivity로 설정 
  • 인터넷 권한 설정
        <!-- 인터넷 권항 선언 -->
        <uses-permission android:name="android.permission.INTERNET"/>
  • userCleartextTraffic="true"로 추가 

2. layout 만들기

화면 구성에 대한 내용을 생략. 아래의 프로젝트 빌드 참고 

 

3. 호스팅


1. 아이비호스팅에서 회원가입
2. 웹 호스팅 -> 무료 호스팅
3. 호스팅 신청하기 
4. 마이페이지 -> 호스팅관리 -> 계정아이디 클릭
  -> 서비스 사용량 란의 [DB관리] 클릭
5. phpMyAdmin으로 와서 MySQL을 이용하여 User 테이블 생성

create table `USER` ( 
	userID varchar(20) not null,
 	userPassword varchar(20) not null, 
	userName varchar(20) not null, 
	userAge int(11) not null, 
	primary key(userID) 
);


6. 호스팅 관리 -> 웹 방화벽 설정 -> 본인 호스팅 계정 석택 -> 웹 방화벽 중지 클릭 -> 설정변경 버튼 !! 필수 


4. Filezilla


FTP에 접속할 수 있게 도와주는 프로그램 

1. 파일질라 설치 후 실행
   => 설치: https://filezilla-project.org/download.php?platform=win64

2. 아이비 호스팅에서 만들었던 호스팅 정보입력 

호스트: 아이비 아이디.ivyro.net
사용자명: 아이비 이름
비밀번호: 아이비 비밀번호
포트: 21 (기본)


3. PHP 연동

FTP 서버로 접속...
public_html 폴더에 Login.php, Register.php 파일을 넣음

Login.php
0.00MB
Register.php
0.00MB

두개의 파일 모두 앞부분의 

<?php 
    $con = mysqli_connect("localhost", "ggp03016", "비밀번호", "ggp03016");

에서
"ggp03016" => "본인의 IB아이디" 로 변경해야함 
"비밀번호" => "본인이 DB를 만들 때 설정한 비밀번호"로 변경해야함

php파일은 DB 통신 수단으로 이해 

php파일 출처: https://duckssi.tistory.com/44 [홍드로이드의 야매코딩]

 

전달되는 데이터 클래스 

Member

package com.example.registerloginexample.Class

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

@Parcelize
data class Member(val userId : String, val userPassword: String, val userName : String, val userAge : Int): Parcelable

@Pacelize를 통해서 직렬화 시켜주고, acitivity간에 이 데이터를 주고 받을 수 있도록 설정해준다. 

자세한설명: https://ggp03016.tistory.com/120

 

5. Service 개발 

RegisterService

package com.example.registerloginexample.Service

class RegisterService {

    private val url: String = "http://ggp03016.ivyro.net/Register.php"

    fun registerRequest(
        context: Context,
        member: Member, 
        success: (Boolean) -> Unit
    ) {
        // 1. RequestQueue 생성 및 초기화
        val requestQueue = Volley.newRequestQueue(context)

        // 2. Request Object인 StringRequest 생성
        val request: StringRequest = object : StringRequest(
            Method.POST, url, Response.Listener { response -> // onResponse 람다
                println("서버 Response 수신: $response")
                val myJson = JSONObject(response)
                success(myJson.getBoolean("success"))
            },
            Response.ErrorListener { error ->
                Log.d("ERROR", "서버 Response 가져오기 실패: $error")
                success(false)
            }) {

            override fun getParams(): MutableMap<String, String> {
                val map = mutableMapOf(
                    "userID" to member.userId,
                    "userPassword" to member.userPassword,
                    "userName" to member.userName,
                    "userAge" to member.userAge.toString()
                )
                return map
            }
        }
        requestQueue.add(request)
    }
}

 

설정한 url의 php파일을 통해서 DB에 접근.

1. Volley를 이용해서 RequestQueue를 생성함 
2. 요청을 보낼 Request를 작성
3. requestQueue에 request를 추가(전달) 

Requset

함수 registerRequest에는 context와 회원가입을 위한 회원 정보(member)와 success라는 람다함수가 있다. 
함수 내부에는 request가 구현되어 있는데, 이는 StringRequest를 상속한 object이다. 서버에 보낼 request가 바로 이 인스턴스 이다. => 서버로부터 문자열 데이터를 전달받음

StringRequest(Method, Url, Response.Listener{}, Response.ErrorListener{})



StringRequest는 오버라이드된 getParams()를 통해서 DB에 request를 키와 값 형태로 전달하고, 처리가 완료되면 response형태를 전달받는다.  

제대로 전달을 받았을 경우에 Response.Listener에 response가 전달이되고 onResponse()의 매개변수 String response에 절달이 되어 json형태로(JSONObject)로 받을 수 있다. (코드에선 람다로 처리)

만약 제대로 전달이 되지 않아서 error가 발생하면 ErrorListener에 에러가 전달이 되고  onErrorResponse()의 매개변수 VolleyError error가 전달되어 처리 할 수 있다. (코드에선 람다로 처리)

 

LoginService

package com.example.registerloginexample.Service

import android.content.Context
import android.util.Log
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import org.json.JSONObject

class LoginService {

    val url = "http://ggp03016.ivyro.net/Login.php"
    lateinit var myJson : JSONObject

    fun loginRequest(context : Context,
                     userId : String, userPassword : String, success : (Boolean) -> Unit
    ){
        val requestQueue = Volley.newRequestQueue(context)

        val request : StringRequest = object : StringRequest(
            Method.POST, url, Response.Listener { response ->
                println("서버 Response 수신: $response")
                myJson = JSONObject(response)
                success(myJson.getBoolean("success"))
            },
            Response.ErrorListener { error ->
                Log.d("ERROR", "서버 Response 가져오기 실패: $error")
                success(false)
            }){

            override fun getParams(): MutableMap<String, String> {
                return mutableMapOf(
                    "userID" to userId,
                    "userPassword" to userPassword
                )
            }
        }
        requestQueue.add(request)
    }
}

마찬가지이며 login을 위한 ID랑 password만 매개변수로 전달받고 php를 통해 전달해준다. 

 

6. Activity, 화면간 이동 처리

 

RegisterActivity

package com.example.registerloginexample.Activity

class RegisterActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_register)

        // 회원 가입 버튼 클릭시 수행
        register_btn_register.setOnClickListener{
            val userId = register_et_id.text.toString()
            val userPassword = register_et_pass.text.toString()
            val userName = register_et_name.text.toString()
            val userAge = register_et_age.text.toString().toInt()
            val member = Member(userId, userPassword, userName, userAge)

            val registerService = RegisterService()
            registerService.registerRequest(
                this,
                member
            ) { testSuccess ->
                if (testSuccess) {
                    Toast.makeText(this, "회원등록에 성공하였습니다.", Toast.LENGTH_SHORT)
                    val intent = Intent(this, LoginActivity::class.java)
                    startActivity(intent)
                } else {
                    Toast.makeText(this, "회원등록에 실패였습니다.", Toast.LENGTH_SHORT)
                }
            }
        }

    }
}

클라이언트가 개인 정보를 다 입력하고 회원가입 버튼을 누르면, 회원가입이 RegistgerService를 통해 진행되고, 성공했을경우 매개변수 함수 success(true)를 수행하고, 실패햇을경우 success(false)를 실행하도록 설정했기 때문에, success함수를  람다형식으로 구현하면 된다.

따라서, 회원가입 서비스 로직은 RegisterService에서 처리하도록, 그리고 이곳에선 성공시, 실패시 화면전환 등 같은 이벤트 처리만 하면 되므로 편리하다. 
위 경우 회원등록에 성공하면 로그인 화면으로, 못한경우 그냥 토스트 메세지만 띄우도록 설정했다. 


LoginActivity

package com.example.registerloginexample.Activity

class LoginActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_longin)

        // 회원가입 버튼 클릭시 수행
        login_btn_register.setOnClickListener{
            val intent = Intent(this, RegisterActivity::class.java)
            startActivity(intent)
        }

        // 로그인 가입 버튼 클릭시 수행
        login_btn_login.setOnClickListener{
            val userId = login_et_id.text.toString()
            val userPassword = login_et_pass.text.toString()
            val loginService = LoginService()
            loginService.loginRequest(this, userId, userPassword) { testSuccess ->
                if (testSuccess) {
                    Toast.makeText(this, "로그인에 성공하였습니다.", Toast.LENGTH_SHORT)
                    val intent = Intent(this, MainActivity::class.java)

                    val userId = loginService.myJson.getString("userID")
                    val userPassword = loginService.myJson.getString("userPassword")
                    val userName = loginService.myJson.getString("userName")
                    val userAge = loginService.myJson.getInt("userAge")
                    val member = Member(userId, userPassword, userName, userAge)

                    intent.putExtra("member", member)
                    startActivity(intent)
                } else {
                    Toast.makeText(this, "로그인에 실패였습니다.", Toast.LENGTH_SHORT)
                }
            }
        }

    }
}


로그인 이후 Member객체인 member를 intent에 넣어서 MainActivity로 전달함

MainActivity

package com.example.registerloginexample.Activity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val member = intent.getParcelableExtra<Member>("member")
        main_et_id.text = member?.userId
        main_et_password.text = member?.userPassword
        main_et_name.text = member?.userName
        main_et_age.text = member?.userAge.toString()

    }
}


전달받은 멤버 변수를 출력함  


7. 이론적인 내용 정리

 

Volley

정의: 안드로이드 앱의 쉽고 빠르게 네트워크 통신을 할 수 있게 해주는 HTTP 라이브러리 

<-> 기존의 HttpURLConnection을 사용하면 직접 스레드를 구현해야하고 기본적인 코드양 또한 많아집니다. (일일히 생성하고 예외처리를 진행해야 하기 때문에) 

RequestQueue는 작업이 이루어지는 스레드를 관리, ex) 네트워크 작업 수행, 캐시 읽기/쓰기 등
사용자는 그냥 RequestQueue에 request를 넣기만 하면됨. Volley의 RequestQueue가 알아서 쓰레드를 생성하여 서버에 요청을 보내고 응답을 받는다. 별도의 thread 관리 작업이 필요 없는 것이다. 

Request는 response의 원본데이터를 파싱하고, Volley는 파싱된 response를 다시 메인스레드로 전달 

  • RequestQueue: 서버 요청자. 다른 Request 클래스들의 정보대로 서버에 요청을 보내는 역할
  • StringRequest: 문자열을 결과로 받는 요청 정보
  • ImageRequest: 이미지를 결과로 받는 요청 정보
  • JsonObjectRequest: JSONObject를 결과로 받는 요청 정보
  • JsonArrayRequest: JSONArray를 결과로 받는 요청 정보

출처: https://kkangsnote.tistory.com/45 [깡샘의 토마토]

Volley의 기능

- 요청 작업 큐와 ThreadPool을 이용한 동시 요청 지원
- 이미지 로딩 툴, NetworkImage View라는 View 제공 
- 요청별 우선 순위 : 목록조회와 이미지 다운로드를 할때 목록조회가 우선순위가 높게 설정. 다음 페이지의 목록조회를 요청하면 이전페이지의 이미지로딩이 끝나지 않아도 기다리지 않고 수행.

<-> Retrofit 라이브러리와 비교했을 때는 속도가 떨어질 수 있다. Retrofit의 기술스택을 학습하는 것도 좋을듯

출처: https://gist.github.com/benelog/5981448

 

Hosting 

제공자 등의 사업자가 주로 [개인 홈페이지의 서버 기능을 대행하는 것]. 
기업의 대용량 메모리 공간 일부를 이용하여 사용자의 홈페이지나 웹 서버 기능을 대행하는 서비스.
사용자는 웹 서버의 운영 관리와 고속 전용선을 상시 사용하므로 회선 사용료의 부담을 줄일 수 있다.사용자가 가진 도메인에서 홈페이지 개설부터 서버 관리까지 대행해주므로 독자 도메인 서비스라고도 한다. 한편, 서버를 갖고 있지 않은 사용자에게 웹 사이트나 서버 기능을 대여하는 것을 렌털 서버(rental server)라고 한다.

출처: [네이버 지식백과] 호스팅 [hosting] (컴퓨터인터넷IT용어대사전, 2011. 1. 20., 전산용어사전편찬위원회)


PHP 


주목적: 원래는 동적 웹 페이지를 만들기 위해 설계되었으며 이를 구현하기 위해 PHP로 작성된 코드를 HTML 소스 문서 안에 넣으면 PHP 처리 기능이 있는 웹 서버에서 해당 코드를 인식하여 작성자가 원하는 웹 페이지를 생성한다. 

PHP는 텍스트, 특히 HTML의 처리에 강점을 가지고 있다. URL의 파싱이나 폼 처리, 정규 표현식 등이 그 한 예이다. 또한 다양한 데이터베이스를 지원하므로 데이터베이스와 사용자간의 다리 역할도 잘 수행한다.

https://ko.wikipedia.org/wiki/PHP


FTP (File Transfer Protocol)


TCP/IP 프로토콜을 가지고 서버와 클라이언트 사이의 파일 전송을 하기 위한 프로토콜이다.

HTTP와는 달리 연결의 종류는 2가지 
- 명령연결: 
먼저 제어 포트인 서버 21번 포트로 사용자, 인증 명령을 위한 연결이 만들어지고 여기를 통해 클라이언트에서 지시하는 명령어가 전달된다. 

- 데이터 전송용 연결: 
실제 파일 전송은 필요할 때 새로운 연결이 만들어진다. 
 -> 능동모드(액티브모드)
  서버는 자신의 데이터 포트인 20번 포트부터 클라이언트가 지정한 지점으로 데이터 연결을 만든다. 클라이언트의 지정포트는 주로 1023보다 큰 번호가 매겨진 포트이다. 
 
 -> 수동모드(패시브모드)
 클라이언트가 서버가 지정한 서버 포트로 연결할 수 있게 한다. 

파생
FTPS: FTP의 확장버전, 클라이언트가 FTP 세션이 암호화 되도록 요청할 수 있게 한다. 

SFTP: SSH파일 전송 프로토콜, 시큐어셸(SSH) 프로토콜을 사용하여 보냄

SFTP(Simple File Transfer Protocol)

출처: https://ko.wikipedia.org/wiki/%ED%8C%8C%EC%9D%BC_%EC%A0%84%EC%86%A1_%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C

 

FileZila


The FileZilla Client not only supports FTP, but also FTP over TLS (FTPS) and SFTP. It is open source software distributed free of charge under the terms of the GNU General Public License.

  • 서버에서 여러 파일을 안전하게 전송하기 위해 FTPS와 SFTP 를 처리합니다. 
  • 이 인터페이스를 통해 모든 전송, 사이트 및 디스크를 신속하게 파악할 수 있으며 끌어서 놓기가 쉽습니다.
  • QuickConnect 버튼 - 몇 초 안에 좋아하는 서버에 액세스할 수 있다. 


FileZilla는 상당히 빠르며 다운로드를 다시 시작하고 4GB보다 큰 매우 큰 전송을 처리 할 수 ​​있습니다. 이미지, 탐색기 파일 또는 CVS 및 SVN 디렉토리와 같은 항목에 대해 로컬 및 원격 필터 를 설정하거나 직접 만들 수도 있습니다. FileZilla에는 파일 크기 또는 수정 시간 측면에서 디렉터리를 비교하는 도구가 포함되어 있습니다.


참고 문헌 및 출처


메인출처:

  • 홍드로이드의 안드로이드 앱만들기 #29 로그인 & 회원가입
    => 이 강의를 코틀린 버전으로, 그리고 layout과 데이터 전달의 형태를 조금씩 달리하여 만들었습니다. 
    https://www.youtube.com/watch?v=ktjJ8xtt2Hg&t=214s
    https://duckssi.tistory.com/44

출처: 

  • [코틀린] VOLLEY 라이브러리 사용예제
  • [Android][Kotlin] 안드로이드 코틀린 Volley
  • [깡샘의 안드로이드 프로그래밍] 정리 27 - Volley API 를 이용한 HTTP 통신
  • https://developer.android.com/training/volley/simple?hl=ko

 

저작자표시 비영리 (새창열림)
    '안드로이드 개발/Kotlin으로 개발' 카테고리의 다른 글
    • Android jetpack: Navigation을 활용하여 Activity 내에 Fragment간의 전환을 쉽게 하기
    • 안드로이드 프로젝트 생성 및 시작
    Per ardua ad astra !
    Per ardua ad astra !
    개발자 지망생이며 열심히 공부하고 기억하기 위한 블로그입니다.

    티스토리툴바