본문 바로가기

카테고리 없음

76일차 공부 Oracle, MyBatis로 서버 만들기 로그인 약간 + android로그인

어제에 이어서

 

8) json으로 출력하는 Controller를 생성하고 메소드를 작성

=> itemDataController

http://localhost:8080/oracleserver/list?pageno=4 입력해보고 데이터가 나오는지 확인

http://localhost:8080/oracleserver/detail?itemid=4 

 

** Android에서 JSON 파싱

1. JSON (JAvascript Object Notation)

=> 자바스크립트의 표현식으로 데이터를 표현하는 표준 포맷

=> 전에는 XML을 표준 포맷으로 많이 사용했는데 JSON이 XML보다는 가볍고 프로그래밍 언어들이 이해하기가 쉬워서 최근에는 JSON을 사용하는 경우가 많다.

 

1) 배열

[데이터, 데이터, 데이터...]

 

2) 객체

{속성이름:데이터, 속성이름:데이터..}

=> 속성이름은 반드시 문자열로 작성해야 하고 데이터는 null, 숫자, boolean, 문자열 그리고 다른 객체나 배열이 가능

 

3) 직접 서버를 구현해서 리턴하는 경우

=> 되도록이면 객체(Map)를 만들어서 리턴

=> List는 이름 대신에 인덱스를 사용해서 데이터를 표현한다.

=> Map은 이름을 이용해서 데이터를 구분

 

 

2. 안드로이드에서의 JSON 파싱

=> java에서는 JSON 파싱을 할려면 JSON라이브러리를 추가해야 하지만 Android에서는 그럴 필요가 없다.

=> 파싱에 사용되는 클래스는 : JSONArray, JSONObject

JSON 문자열의 구조를 보고 생성자를 이용해서 생성

 

=> 가져온 문자열을 보고 아래 중 1개로 생성

JSONArray 이름 = new JSONArray(JSON 문자열);

JSONObject 이름 = new JSONObject(JSON 문자열);

 

=> 2개의 클래스에는 각각의 데이터를 가져오는 메소드가 존재하는데 매개변수가 배열은 인덱스이고 객체는 속성 이름이다.

 

배열.get자료형(0) : 0번째 데이터를 자료형으로 가져오는 것

객체.get자료형("속성이름") : 속성이름에 해당하는 데이터를 자료형으로 가져온다.

 

=> item와 itemname 만 가져와서 출력하기

JSONObjec obj = new JSONObject(data); //데이터가 { 로 시작

JSONArray list = obj.getJSONArray("list"); // list에 해당하는 데이터를 배렬로 가져오기

for(int i =0; i<list.length(); i=i+1) {

    JSONObject item = list.getJSONObject(i);

    int item = item.getInt("itemid");

    int itemname = item.getString("itemname");

}

 

** 서버의 데이터를 가져와서 출력하기 - Android

http://192.168.0.200:8080/list?pageno=페이지번호

http://192.168.0.200:8080/list?itemid=아이템아이디

 

1. Android Application Project 생성

 

2. 인터넷 사용 권한을 설정

=> 네트워크를 사용할 때는 INTERNET 권한이 있어야 한다.

=> 웹 서버에 접속할 때 http 프로토콘을 사용하는 경우에는 Application에 android:usesCleartextTraffic=true로 설정되어 있어야 한다.

 

<uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:usesCleartextTraffic="true"

 

버튼을 누르면 첫번째 페이지의 데이터를 가져와서 TextView를 출력하고 다시 누르면 다음 페이지의 데이터를 추가

 

3. activity_main.xml 파일에 화면 디자인

=> 버튼 1개와 TextView 1개를 배치 - TextView의 너비를 데이터가 초과?

=> 출력되는 데이터가 View의 크가보다 크면 View에 내용을 전부 출력할 수 없어서 이런 경우에는 ScrollView를 사용해야 한다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="데이터 가져오기"
        android:id="@+id/btn"/>
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/display"/>
    </ScrollView>

</LinearLayout>

 

 

4. activity.java 파일에 작성

 

package com.example.androiddataparsing;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class MainActivity extends AppCompatActivity {
    private TextView display;
    private Button btn;
    //페이지 번호를 저장할 변수
    int pageno = 1;

    //텍스트뷰에 출력할 데이터를 저장할 변수
    //ListView에 출력하는 경우라면 ArrayList, ListAdapter 생성
   String msg = "";

    //데이터를 다운로드 받을 스레드 클래스
    class ThreadEx extends Thread{
        @Override
        public void run(){
            try{
                //다운로드 받을 URL 생성
                URL url = new URL("http://192.168.0.200:8080/oracleserver/list?pageno=" + pageno);
                //연결 객체를 생성하고 옵션 설정
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                con.setConnectTimeout(30000);
                con.setUseCaches(false);
                //문자열 읽어오기
                BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                StringBuilder sb = new StringBuilder();
                while(true){
                    String line = br.readLine();
                    if(line ==null){
                        break;
                    }
                    sb.append(line + "\n");
                }
                //읽어온 데이터를 msg에 추가
                //msg = msg + sb.toString();

                //읽어온 데이터를 파싱하기
                JSONObject object = new JSONObject(sb.toString());
                //list 키 안의 배열을 찾아오기
                JSONArray list = object.getJSONArray("list");
                //배열을 순회
                for(int i=0; i<list.length(); i=i+1){
                    //배열에서 i번째 데이터 가져오기
                    JSONObject item = list.getJSONObject(i);
                    int itemid = item.getInt("itemid");
                    String itemname = item.getString("itemname");
                    msg = msg + itemid + ":" + itemname + "\n";
                }

                //핸들러에게 출력 요청
                Message message = new Message();
                message.obj= msg;
                handler.sendMessage(message);

            }catch(Exception e){
                Log.e("다운로드 에러", e.getMessage());
            }
        }
    }

    //다운로드 받은 후 데이터를 재출력하는 핸들러
    Handler handler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message message){
            String data = (String)message.obj;
            display.setText(data);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        display = (TextView)findViewById(R.id.display);
        btn = (Button)findViewById(R.id.btn);

        //스레드를 생성해서 데이터를 출력
        ThreadEx th = new ThreadEx();
        th.start();

        //버튼을 클릭했을 때 다음 페이지 데이터 추가하기
        btn.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View view) {
                //페이지 번호 증가
                pageno = pageno +1;
                //스레드를 생성해서 데이터를 출력
                ThreadEx th = new ThreadEx();
                th.start();
            }
        });
    }
}

 

 

5. 실행 가능한 Activity 추가(DetailActivity)

 

 

6. activity_layout.xml 파일에 화면 디자인

=> 텍스트뷰 1개와 이미지뷰 1개를 배치

=> 아이템이름과 이미지를 출력

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".DetailActivity"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/itemname"/>
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/pictureurl"/>

</LinearLayout>

 

 

7. Server Application 의 servlet-context.xml 파일에 설정 추가

 

<!-- Controller가 처리하지 못하는 URL은 WAS가 처리하도록 하는 설정 -->
	<default-servlet-handler/>

 

 

8. DetailActivity.java 파일에 코드 작성

 

package com.example.androiddataparsing;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

public class DetailActivity extends AppCompatActivity {
    TextView itemname;
    ImageView pictureurl;

    //itemname에 출력할 텍스트
    String name;
    //ImageView에 출력할 이미지 url
    String image;

    class ThreadEx extends Thread{
        public void run(){
            //다운로드 받은 문자열을 저장할 변수
            String result = null;
            try{
                URL url = new URL("http://192.168.0.200:8080/oracleserver/detail?itemid=" + 1);
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                con.setUseCaches(false);
                con.setConnectTimeout(30000);

                BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                StringBuilder sb = new StringBuilder();
                while(true){
                    String line = br.readLine();
                    if(line == null)
                        break;
                    sb.append(line + "\n");
                }
                result = sb.toString();
                br.close();
                con.disconnect();

            }catch(Exception e){
                Log.e("다운로드 예외", e.getMessage());
            }

            //가져온 데이터 파싱
            try{
                JSONObject object = new JSONObject(result);
                JSONObject item = object.getJSONObject("item");
                //itemname 과 pictureurl 만 가져와서 저장
                name = item.getString("itemname");
                image = item.getString("pictureurl");

                //핸들러 호출
                Message message = new Message();
                handler.sendMessage(message);

            }catch(Exception e){
                Log.e("파싱 예외", e.getMessage());
            }
        }
    }

    Handler handler = new Handler(Looper.getMainLooper()){
        public void handleMessage(Message message){
            itemname.setText(name);
            //이미지를 다운로드 받기 위해서 이미지 스레드를 실행
            new ImageThread().start();
        }
    };
    class ImageThread extends Thread{
        public void run(){
            try{
                URL url = new URL("http://192.168.0.200:8080/oracleserver/img/" + image);
                InputStream is = url.openStream();
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                is.close();
                //핸들러를 호출하기
                Message message = new Message();
                message.obj = bitmap;
                imageHandler.sendMessage(message);
            }catch(Exception e){
                Log.e("이미지 다운로드 예외", e.getMessage());
            }
        }
        Handler imageHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message message) {
                Bitmap bitmap = (Bitmap)message.obj;
                pictureurl.setImageBitmap(bitmap);
            }
        };
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);

        itemname = (TextView)findViewById(R.id.itemname);
        pictureurl = (ImageView)findViewById(R.id.pictureurl);

        new ThreadEx().start();
    }
}

 

 

** 로그인 및 로그아웃

1. 회원테이블을 생성하고 샘플 데이터를 대입(오라클에서)

create table member(
	id varchar2(100) primary key,
	pw varchar2(100) not null);
    
insert into member(id, pw) values('lsb5212', '1234');

 

2. 서버 작업

=> domain/member.java 클래스 생성

 

2) Member 테이블의 SQL 작업을 위한 mapper 파일을 추가

=>src/main/resources/mybatis/mappers/member.xml

 

3) Member 테이블에 SQL을 수행할 MemberDAO 클래스를 생성하고

 

4) Member 테이블 요청을 위한 메소드를 소유한 MemberService인터페이스를 생성

 

package kr.co.nfl.quarterback.service;

import javax.servlet.http.HttpServletRequest;

public interface MemberService {
	public void login(HttpServletRequest request);
}

 

5) Member 테이블 요청을 처리하기 위한 메소드를 구현한 MemberServiceImpl 클래스를 생성

 

package kr.co.nfl.quarterback.service;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import kr.co.nfl.quarterback.dao.MemberDAO;
import kr.co.nfl.quarterback.domain.Member;

@Service
public class MemberServiceImpl implements MemberService {
	@Autowired
	private MemberDAO memberDao;
	
	@Override
	public void login(HttpServletRequest request) {
		//파라미터 읽기 - 다 못쓴 파라마터 나중에 꼭 추가해주자.
		String email = request.getParameter("email");
		String password = request.getParameter("password");
		
		//DAO의 매개변수 만들기
		Member member = new Member();
		member.setEmail(email);
		member.setPassword(password);
		
		//DAO의 메소드를 호출
		Member result = memberDao.login(member);
		
		//result가 null이면 로그인 실패, 그렇지 않으면 로그인 성공
		request.setAttribute("result", result);

	}

}

 

 

6) Controller 클래스를 추가하고 로그인 처리 메소드를 생성

 

package kr.co.nfl.quarterback.controller;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import kr.co.nfl.quarterback.domain.Member;
import kr.co.nfl.quarterback.service.MemberService;

@RestController
public class MemberDataController {
	@Autowired
	private MemberService memberService;
	
	@RequestMapping(value="login")
	public Map<String, Object> login(HttpServletRequest request){
		memberService.login(request);
		Member member = (Member)request.getAttribute("result");
		Map<String, Object> map = new HashMap<String, Object>();
		//어떤 작업 수행의 결과가 null 일 수 있는 경우는 다른 키를 하나 추가해서 null 여부를 판단할 수 도록 해준다.
		if(member == null) {
			map.put("login", false);
		}else {
			map.put("login", true);
			map.put("result", member);
		}
		return map;
	}
}

 

테스트

http://localhost:8080/oracleserver/login?email=lsb5212&password=1234

oracleserver는 내껄로 고쳐서해보자.

 

 

3. Android

1) 실행가능한 Activity 생성(LoginActivity)

 

 

 

2) 레이아웃 수정(activity_login.xml)

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".LoginActivity"
    android:orientation="vertical">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="이메일을 입력하세요"
        android:id="@+id/emailinput"/>
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="비밀번를을 입력하세요"
        android:id="@+id/passwordinput"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="로그인"
        android:id="@+id/btnlogin"/>

</LinearLayout>

 

 

3) LoginActivity .java 파일에 작성

 

package com.example.androiddataparsing;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class LoginActivity extends AppCompatActivity {
    private EditText emailinput, passwordinput;
    private Button btnlogin;

    class ThreadEx extends Thread{
        @Override
        public void run(){
            String json = null;
            try{
                URL url = new URL(
                        "http://192.168.0.200:8080/oracleserver/login?"
                                +"email=" + emailinput.getText().toString() + "&password=" +
                                passwordinput.getText().toString());
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                con.setUseCaches(false);
                con.setConnectTimeout(30000);

                BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                StringBuilder sb = new StringBuilder();
                while(true){
                    String line = br.readLine();
                    if(line == null)
                        break;
                    sb.append(line + "\n");
                }
                json = sb.toString();
                Log.e("json", json);

            }catch(Exception e){
                Log.e("다운로드 에러", e.getMessage());
            }
            try{
                JSONObject object = new JSONObject(json);

                Message message = new Message();
                message.obj = object;
                handler.sendMessage(message);
            }catch (Exception e){
                Log.e("파싱 에러", e.getMessage());
            }
        }
    }

    Handler handler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message message){
            try {
                JSONObject result = (JSONObject) message.obj;
                boolean r = result.getBoolean("login");
                if (result == null) {
                    Toast.makeText(LoginActivity.this, "로그인 실패", Toast.LENGTH_LONG).show();
                } else {
                    ShareData.login = true;
                    //로그인 성공했을 때 email과 password를 파일에 저장하기
                    try{
                        //파일을 생성
                        FileOutputStream fos = openFileOutput("login.txt", Context.MODE_PRIVATE);
                        fos.write(emailinput.getText().toString().getBytes());
                        fos.write(":".getBytes());
                        fos.write(passwordinput.getText().toString().getBytes());
                        fos.close();
                    }catch (Exception e){}
                    Toast.makeText(LoginActivity.this, "로그인 성공", Toast.LENGTH_LONG).show();
                }
            }catch (Exception e){}
        }
    };

    //액티비티를 터치했을 때 호출되는 메소드
    @Override
    public boolean onTouchEvent(MotionEvent event){
        //키보드 내리기 - 이걸 안하면 키보드가 안없어지고 유지되어 버린다.
        InputMethodManager imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(emailinput.getWindowToken(), 0);
        imm.hideSoftInputFromWindow(passwordinput.getWindowToken(), 0);
        return true;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        Toast.makeText(this, ShareData.login+"", Toast.LENGTH_LONG).show();
        emailinput = (EditText)findViewById(R.id.emailinput);
        passwordinput = (EditText)findViewById(R.id.passwordinput);
        btnlogin = (Button)findViewById(R.id.btnlogin);

        try{ //이렇게 파일에 저장하면 다시 로그인 할 필요가 없다.
            FileInputStream fis = openFileInput("login.txt");
            byte [] data = new byte[fis.available()];
            fis.read(data);
            Toast.makeText(this, new String(data), Toast.LENGTH_LONG).show();
        }catch (Exception e){
            Toast.makeText(this, "로그인 한 적 없음", Toast.LENGTH_LONG).show();
        }

        btnlogin.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View view) {
                new ThreadEx().start();

                //키보드 내리기 - 이걸 안하면 키보드가 안없어지고 유지되어 버린다.
                InputMethodManager imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(emailinput.getWindowToken(), 0);
                imm.hideSoftInputFromWindow(passwordinput.getWindowToken(), 0);
            }
        });
    }
}

 

 

** 로그인 처리를 할 때 웹에서는 세션이나 데이터베이스를 이용해서 로그인 정보를 저장해서 로그인 여부를 판정하게 된다.

모바일에서는 세션이 없기 때문에 다른 방법을 생각해야 한다.

 

1. 메모리 상에서의 공유 : 애플리케이션이 동작 중인 동안만 데이터가 유지

=> public class 에 static 변수를 생성해서 이용

=> sington 패턴으로 디자인하고 인스턴스 변수를 만들어서 사용

 

2. 반영구적으로 저장하기

=> 파일에 저장

=> sqlite에 저장

=> 환경설정에 저장

 

3. 서버에 저장

=> 네트워크가 안될 때 서버의 데이터를 가져올 수가 없다.