본문 바로가기

카테고리 없음

75 안드로이드 Network, MyBatis Oracle 서버

** Socket

=> 네트워크 인터페이스 카드를 추상화 한 클래스

저수준 통신 : Socket을 직접 생성해서 통신 - Socket 통신

고수준 통신 : Socket을 직접 생성하지 않고 추상화된 클래스를 이용

 

저수준 통신이 효율은 좋지만 어려움

 

1. java.net.InetAddress

=> 인터넷 주소와 관련된 클래스

=> 생성자는 없고 static 메소드를 이용해서 인스턴스를 생성

getLocalHost, getByName, getAllByName을 이용해서 생성

예외처리를 강제한다.

 

2. java.net.Socket 클래스

1) 생성자

Socket()

Socket(InetAddress adds, int port)

Socket(String addr, int port)

Socket(InetAddress addr, int port, InetAddress localAddr, int localPort) : 앞의 두개는 접속할 상대방의 주소정보, 뒤의 두개는 자신의 주소 정보 - Network Interface Card가 2개 이상인 경우

=> 상대방 주소를 설정해서 생성하면 자동으로 접속이 된다.

addr이 잘못되면 NullPointerException이 발생하고 port번호가 잘못되면 IllegalArgumentException이 발생한다.

 

2) 입출력을 위한 스트림을 제공하는 메소드

InputStream getInputStream()

OutputStream getOutputStream()

 

3) UDP 와 TCP

=> UDP : 비연결형 통신 - 데이터를 일방적으로 보내기만 하는 방식

효율은 좋은데 신뢰성이 떨어짐 - APNS, FCM(스마트폰의 알림 메시지)

 

=> TCP : 연결형 통신 - 서로 연결을 하고 대화를 주고 받는 방식으로 통신

효율은 UDP에 비해 떨어지지만 신뢰성이 높다.

 

=> java에서는 일반 소켓과 ServerSocket을 이용하면 TCP 통신이고 DatagramSocket을 이용하면 UDP 통신이다.

 

** Android와 PC Application TCP Socket 통신

1. Server 역할을 할 PC Application

1) Java Project 생성 - STS에서 작업

 

2) Main 메소드를 소유한 클래스를 추가

 

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

	public static void main(String[] args) {
		try {
			//포트번호 설정
			//1024번 아래 8080, 1521, 3306은 제외하고 설
			int portNumber = 11000;
			System.out.println("서버 실행중....");
			//서버 소켓 생성 - 접속 준비
			ServerSocket ss= new ServerSocket(portNumber);
			while(true) {
				System.out.println("서버 대기 중....");
				//클라이언트의 접속 대기
				//여기서 블럭 되어 있다가 클라이언트가 접속하면 클라이언트와 통신할 수 있는 소켓을 리턴하고 아래 문장을 수행
				Socket socket = ss.accept();
				//클라이언트 정보 출력
				System.out.println(socket.getInetAddress() + ":" + socket.getPort());
				//객체를 읽어오는 Stream 생성
				ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
				//하나의 객체 읽기
				Object obj = ois. readObject();
				System.out.println(obj);			
				
				ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
				
				PrintWriter pw = new PrintWriter(socket.getOutputStream());
				//한 줄 메시지 전송 - 버퍼에 기록
				switch(obj.toString()) {
				case "1":
					oos.writeObject("일번");
					break;
				case "2":
					oos.writeObject("이번");
					break;
				default:
					oos.writeObject("기타");
					break;
				}
				oos.writeObject("서버가 보내는 메시지");
				oos.flush();
				//소켓 종료
				socket.close();				
			}			
		}catch(Exception e) {
			System.out.println(e.getLocalizedMessage());
			e.printStackTrace();
		}

	}

}

 

 

2. Android Application

1) 프로젝트 생성

 

2) 화면 디자인

=> 전송할 문자열을 입력받을 EditText 1개와 전송받은 문자열을 출력할 TextView 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=".MainActivity"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:id="@+id/receive" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/send"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="전송"
        android:id="@+id/btn"/>

</LinearLayout>

 

 

3) Activity 에 코드 작성

 

package com.example.android0722;

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.EditText;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.Socket;

public class MainActivity extends AppCompatActivity {
    //뷰의 참조를 저장하기 위한 변수
    EditText send;
    TextView receive;
    Button btn;

    //네트워크 사용을 위한 스레드 클래스
    //스레드는 한 번 사용하면 새로 생성해야 사용이 가능하다
    //class ThreadEx extends Thread{  여러번 보낼때
    //Thread th = new Thread(){  한번 보낼 때
    class ThreadEx extends Thread{
        @Override
        public void run(){
            String content = null;
            try{
                //서버에 접속하는 소켓생성
                Socket socket = new Socket("192.168.0.200", 11000);
                //스트림 생성
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                oos.writeObject(send.getText().toString());
                oos.flush();

                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                content = (String)ois.readObject();
                //스레드에서는 UI 갱신을 못한다.
                //receive.setText(content);
                socket.close();
            }catch (Exception e){
                Log.e("전송 에러", e.getMessage());
            }
            //핸들러에게 전송할 메시지 생성
            Message msg = new Message();
            msg.obj = content;
            //핸들러 호출
            handler.sendMessage(msg);
        }
    };
    //스레드가 전송한 내용을 출력하기 위한 핸들러
    Handler handler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg){
            String temp = (String)msg.obj;
            receive.setText(temp);
        }
    };

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

        send = (EditText)findViewById(R.id.send);
        receive = (TextView)findViewById(R.id.receive);
        btn = (Button)findViewById(R.id.btn);

        //버튼의 클릭이벤트 처리
        btn.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View view) {
                //스레드 시작
                //th.start();한번 보낼때
                new ThreadEx().start();
            }
        });

    }
}

 

 

4) 안드로이드에서는 네트워크를 사용하기 위한 권한 설정

=> INTERNET

<uses-permission android:name="android.permission.INTERNET"/>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android0722">

    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

 

** URL 통신

=> 웹의 주소를 이용하는 통신 방식

=> http나 https 프로토콜을 이용하는 통신 방식

=> 스마트폰은 보안 문제 때문에 http는 추가 설정을 해야만 통신이 가능

1. URL 클래스

=> 접속할 주소를 만들어주는 클래스

1) 생성자

URL(String addr) : new URL("https://www.daum.net")

URL(String protocol, String host, int port, String file) : new URL("https", "www.daum.net", 443, "/");

 

2) 메소드

=> get으로 시작하는 메소드를 이용해서 각 부분의 정보를 리턴받을 수 있다.

 

3) 주의할 점

=> 잘못된 URL을 대입하면 MalformedURLException이 발생

=> URL은 전부 인코딩 된 상태로 생성해야 한다.

URL에 영문이나 숫자 이외의 데이터가 있다면 인코딩을 해 주어야 한다.

URLEncoder.encode(변환할 문자열, "utf-8")

 

2. URLConnection

=> URL에 접속해서 서버에게 데이터를 전송하고 읽어올 수 있는 클래스

1) 객체 생성

(실제 사용할 클래스)URL.openConnection();

=> 실제 사용할 클래스는 HttpURLConnection, HttpsURLConnection, JarURLConnection

 

2) 옵션을 설정하는 메소드

=> setConncetTimeout(int timeout) : 접속이 안될 때 최대 시도 시간으로 설정하지 않으면 무제한

=> setUserCache(boolean newValue) : 변화가 별로 없는 경우 브라우저의 캐시 기능처럼 데이터를 로컬에 저장했다가 다시 불러오기 위한 옵션

=> post 방식의 파라미터나 헤더를 추가하는 메소드

setRequestProperty(String attr, String value)

addRequestProperty(String attr, String value)

=> 전송방식 선택 : setRequestMethod("GET" 또는 "POST") : 기본은 GET

=> 요청 결과 리턴 : getResponseCode()

 

GET : 파라미터를 URL에 포함해서 전송, 보안이 취약, 전송하는 크기에 제한(255자)이 있다.

 

POST : 파라미터를 header에 숨겨서 전송, 보안이 우수, 전송하는 크기에 제한이 없다.

인코딩을 잘 설정해야 하고 속도는 GET보다는 느리다. 최근에는 FORM의 데이터는 되도록 POST로 전송하는 것을 권장

Password, textarea, file 이 존재하는 경우는 무조건 POST

 

응답 코드

200 : 정상 응답

300 : 리다이렉트 중

400 : 클라이언트 오류(주소, 권한 등의 오류)

500 : 서버 오류(서버의 로직이 잘못 수행되는 경우)

 

3) Stream을 리턴해주는 메소드

InputStream getInputStream()

OupputStream getOutputStream()

 

4) http 통신의 문제

=> 보안이 취약하기 때문에 기본적으로 접속이 안되도록 되어 있다

=> 안드로이드에서는 application 설정에 android:usesClearTraffic="true"를 추가해주어야 한다.

 

** 텍스트를 읽어서 출력하기

1. 실행가능한 Activity 추가

 

2. 화면 디자인

=> 버튼 1개 배치하고 HTML을 출력할 TextView를 배치하는데 HTML이 TextView를 넘어가면 스크롤 할 수 있도록 TextView를 ScrollView안에 배치

=> WebView, ListView(데이터 출력을 위한 뷰), MapView는 ScrollView를 상속받아서 자체적으로 스크롤이 가능한데 그 이외의 View는 스크롤 기능이 없으므로 내용이 많은 경우에는 스크롤 뷰 안에 배치해야 한다.

 

<?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=".TextDownloadActivity"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:text="다운로드"
        android:id="@+id/download"/>
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="HTML 출력"
            android:textSize="20sp"
            android:id="@+id/display"/>
    </ScrollView>

</LinearLayout>

 

 

3. Activity.java 파일 작성

 

package com.example.android0722;

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 java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class TextDownloadActivity extends AppCompatActivity {
    private TextView display;
    private Button download;

    //데이터를 다운로드 받을 클래스
    //Thread는 재사용이 안되므로 클래스로 만들어서
    //필요할 때 마다 객체를 생성해서 사용
    class ThreadEx extends Thread{
        public void run(){
            //다운로드 받은 문자열을 저장할 변수
            String html = null;
            try{
                //다운로드 받을 주소 생성
                URL url = new URL("https://www.google.com");
                //연결 객체를 생성하고 옵션을 설정
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                con.setUseCaches(false);
                con.setConnectTimeout(30000);
                con.setRequestMethod("GET");
                //필요한 스트림을 생성해서 읽기
                //문자열을 읽을 스트림 생성
                BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                StringBuilder sb = new StringBuilder();
                while(true){
                    //한 줄 읽기
                    String line = br.readLine();
                    //읽은 내용이 없으면 종료
                    if(line == null){
                        break;
                    }
                    //읽은 내용이 있으면 sb에 추가
                    sb.append(line + "\n");
                }
                html = sb.toString();
                //생성한 객체 닫기
                br.close();
                con.disconnect();

                //UI 갱신 할 거라면 Handler를 호출해서
                //데이터를 넘겨주기
                Message message = new Message();
                message.obj = html;
                handler.sendMessage(message);

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

    //데이터를 출력하기 위한 객체
    //재사용이 가능하므로 하나의 객체를 바로 생성해서 사용
    Handler handler = new Handler(Looper.getMainLooper()){
                public void handleMessage(Message message){
                    //데이터 가져오기
                    String html = (String)message.obj;
                    display.setText(html);
                }
            };


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

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

        download.setOnClickListener(
                new Button.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        new ThreadEx().start();
                    }
                });
    }
}

 

 

4. AndroidManifest.xml 파일에서 권한 확인

=> INTERNET 권한이 있어야 하고 접속하는 곳이 http로 시작하는 경우에는 application에 옵션을 추가해야 한다.

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android0722">

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

    <application
        android:usesCleartextTraffic="true"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".TextDownloadActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

 

 

** 데이터 다운로드 받아서 출력할 때 주의 사항

=> 권한 설정을 하지 않으면 permit 관련된 에러

=> 다운로드 받는 코드를 Thread에 작성하지 않으면 앱은 Crash

=> UI 갱신을 스레드에서 직접하면 아무일도 발생하지 않는다.

 

** 이미지 파일 다운로드

=> 파일을 다운로드 받는 것은 BufferedReader 대신에 BufferedInputStream을 사용해서 읽어온 후 파일에 기록

=> 파일은 계속해서 다운로드 받지 않고 대부분의 경우는 처음 덥속할 대만 다운로드받아서 저장하고 이후에는 저장된 데이터가 있는지 확인해서 없으면 다운로드하고 잇으면 로컬에 있는 파일을 사용

=> 파일이 업데이트될 가능성이 있다면 서버를 만들 때 업데이트 날짜를 기록을 해야 하고 앱은 처음 접속할 때 파일이 있는지 확인한 후 없으면 다운로드 받고 있으면 파일의 마지막 수정 날짜와 서버의 업데이트 날짜를 비교해서 서버의 업데이트 날짜가 더 크다면 다시 다운로드를 받고 그렇지 않으면 로컬에 있는 파일을 사용

=> 안드로이드 앱은 자신의 데이터 디렉토리 에만 파일을 저장할 수 있다.

/data/data/패키지이름/files 디렉토리가 자신의 데이터를 저장할 수 있는 디텍토리

맨 앞의 /data 대신에 Environment.getDataDirectory().getAbsolutePath()를 이용해서도 생성 가능

 

** 이미지 파일을 다운로드 받아서 바로 ImageView에 출력하는 것과 파일로 저장하고 출력하기

 

1. 실행 가능한 Activity 생성

 

2. 화면 디자인

 

<?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=".ImageActivity"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="이미지 바로 출력"
        android:id="@+id/drawImage"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="이미지 저장 후 출력"
        android:id="@+id/saveimage"/>
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/imageview"/>

</LinearLayout>

 

 

3. Activity.java파일 

 

package com.example.android0722;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.renderscript.ScriptGroup;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class ImageActivity extends AppCompatActivity {
    private ImageView imageView;
    private Button drawimage, saveimage;

    //이미지를 다운로드 받아서 바로 출력할 스레드와 핸들러
    class DrawThread extends Thread{
        @Override
        public void run(){
            try{
                //이미지 URL 만들기
                URL url = new URL("https://t1.daumcdn.net/cfile/tistory/274E06365521501A27");
                //이미지의 스트림을 생성
                InputStream is = url.openStream();
                //비트맵으로 변환
                Bitmap bitmap = BitmapFactory.decodeStream(is);

                //핸들러에게 전송
                Message msg = new Message();
                msg.obj = bitmap;
                drawHandler.sendMessage(msg);
            }catch (Exception e){
                Log.e("다운로드 에러", e.getMessage());
            }
        }
    }

    Handler drawHandler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message message){
            Bitmap bitmap = (Bitmap)message.obj;
            imageView.setImageBitmap(bitmap);
        }
    };

    class SavaThread extends Thread{
        public void run(){
            try{
                URL url = new URL("http://192.168.0.200:8080/image/1.png");
                HttpURLConnection con =(HttpURLConnection)url.openConnection();
                con.setUseCaches(false);
                con.setConnectTimeout(30000);

                //바이트 스트림 생성
                InputStream is = con.getInputStream();
                //다운로드 받은 내용을 저장할 배열을 생성
                byte [] raster = new byte[con.getContentLength()];
                //파일의 내용을 읽어서 raster에저장하고 파일에 기록
                FileOutputStream fos = openFileOutput("i.png", 0);
                while(true){
                    int read = is.read(raster);
                    if(read <= 0){
                        break;
                    }
                    fos.write(raster, 0, read);
                }
                is.close();
                fos.close();
                con.disconnect();
            }catch (Exception e){
                Log.e("다운로드 예외", e.getMessage());
            }
            Message message = new Message();
            message.obj = "1.png";
            saveHandler.sendMessage(message);
        }
    }

    Handler saveHandler = new Handler(Looper.getMainLooper()){
        public void handleMessage(Message message){
            String filename = (String)message.obj;
            String imagePath = Environment.getDataDirectory().getAbsolutePath()
                    + "/data/com.example.android0722/files/" + filename;
            imageView.setImageBitmap(BitmapFactory.decodeFile(imagePath));
        }
    };

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

        imageView = (ImageView)findViewById(R.id.imageview);
        drawimage = (Button)findViewById(R.id.drawImage);
        saveimage= (Button)findViewById(R.id.saveimage);

        Button.OnClickListener listener =new Button.OnClickListener() {
            @Override
            public void onClick(View view) {
                switch (view.getId()){
                    case R.id.drawImage:
                        new DrawThread().start();
                        break;
                    case R.id.saveimage:
                        //자신의 data 디렉토리에 이미지 파일이 있는지 확인
                        String imagePath = Environment.getDataDirectory().getAbsolutePath()
                                + "data/com/example.android0722/files/1.png";
                        //File 객체 생성
                        File file = new File(imagePath);
                        if(file.exists()){
                            Toast.makeText(ImageActivity.this, "파일이 존재", Toast.LENGTH_LONG).show();
                            //파일의 내용 출력
                            imageView.setImageBitmap(BitmapFactory.decodeFile(imagePath));
                        }else{
                            Toast.makeText(ImageActivity.this, "파일이 존재하지 않음", Toast.LENGTH_LONG).show();
                            //다운로드 받아서 파일로 저장한 후 출력
                            new SavaThread().start();
                        }
                        break;
                }
            }
        };
        drawimage.setOnClickListener(listener);
        saveimage.setOnClickListener(listener);
    }
}

 

 

 

** Oracle & MyBatis를 이용한 Spring MVC Project와 Android 통신

=> 데이터 조회

 

1. Oracle에 접속해서 샘플 데이터 생성

=> URL : 192.168.0.76

=> port : 1521

=> id

=> pw

 

 

-- item 별로 내림차순 정렬한 후 2개의 데이터 가져오기 

-- 원하는 위치의 데이터를 가져올 때 >=시작위치 and <= 종료위치

select * 

from (select rownum rnum, itemid, itemname, price, description, pictureurl 

    from(select * from item order by itemid desc))

where rnum >=1 and rnum <=2

 

2. Spring MVC Project 생성

 

3. pom.xml(Maven 설정파일이다.) 파일에 필요한 의존성 설정 

1) java와 spring 그리고 junit 버전을 변경

<java-version>1.8</java-version>

<org.springframework-version>5.0.7.RELEASE</org.springframework-version>

 

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

 

<configuration>

<source>1.8</source>

<target>1.8</target>

 

=> 여기까지 저장하고 실행한번 해본다.

에러가 발생하는 경우는 대부분 포트 충돌이다.

다른 톰캣이 실행중이 아닌지 확인하고 자신의 컴퓨터에 오라클이 설치된 경우면 servers 디렉토리에서 server.xml을 열어서 포트번호가 8080인 부분을 다른 번호로 수정

port 번호가 -1인게 있으면 8005 번으로 수정

 

2) Oracle 을 사용하는 경우는 repositories를 설정

=> repositories는 중앙저장소가 아닌 곳에서 다운로드 받을 때 설정

=> Oracle은 Maven 중앙 저장소에 없다.

 

3) 필요한 의존성을 dependencies 안에 추가

=> oracle, mysql, spring-jdbc, spring-orm, mybatis, mabatis-spring, hibernate : 데이터베이스 연동 필수 라이브러리

 

=> json 라이브러리 : json 출력 스프링뷰.pdf 파일에 있다.

=> spring-test : junit을 이용해서 spring project 테스트

=> common-fileupload : 파일 업로드 spring mVC기타에 있다.

=> jbcrypt : 복호화가 불가능한 암호화  "

=> lombok : getter&setter, toString 만들기 관리해주는 라이브러리

 

 

		<!-- 오라클 -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc7</artifactId>
			<version>12.1.0.2</version>
		</dependency>

		<!-- spring jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.6</version>
		</dependency>

		<!-- mybatis-spring -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.2</version>
		</dependency>

		<!-- 하이버네이트 사용 의존성 -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>5.4.2.Final</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- json 출력을 위한 라이브러리 -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.9.7</version>
		</dependency>

		<!-- file upload API -->
		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.3.1</version>
		</dependency>

		<!-- 복호화가 불가능한 암호화 라이브러리 -->
		<dependency>
			<groupId>org.mindrot</groupId>
			<artifactId>jbcrypt</artifactId>
			<version>0.4</version>
		</dependency>
		
		<!-- lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.8</version>
		</dependency>

		<!-- 데이터베이스 로그 기록 -->
		<dependency>
			<groupId>org.bgee.log4jdbc-log4j2</groupId>
			<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
			<version>1.16</version>
		</dependency>

		<!-- 스프링 테스트 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

 

4) web.xml(웹 프로젝트 설정 파일) 에 인코딩 필터를 설정

=> 이 작업을 하지 않으면 입력할 때 한글을 입력하면 한글이 깨진다.

=> 한글이 깨지면 인코딩 필터 설정과 MySQL 테이블 만들 때 인코딩 설정을 했는지 그리고 MySQL DataSource 만들 때 인코딩 설정했는지 확인

	<!-- 파라미터 인코딩 필터 설정 -->
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

=> web.xml 설정하고 실행해본다.

 

5. 파일 업로드가 있는 경우 파일을 저장할 디렉토리를 생성

=> webapp 기준

=> img 와 profile 디렉토리 생성

 

6. 하나의 서비스를 위한 준비

=> 테이블과 연동할 DTO

=> 테이블에 수행할 SQL을 저장하는 mapper(mybatis) : mybatis/mappers/  ...xml

     테이블과 매핑할 객체에 대한 설정을 하는 xml(hibernate) : dao/item.hbm.xml

=> DAO : ItemDAO

=> Service, Serviceimpl ; ItemService, ItemServiceImpl

=> controller(웹 브라우저 화면 출력 용도), RestController(데이터를 전송하기 위한 용도) : ItemViewController, ItemDataController

=> View(웹 브라우저 화면 출력 용도)

 

=> 모든 클래스들은 기본 패키지 안에 생성

기본 패키지 이름을 변경한 경우에는 servlet-context.xml 파일에서 component-scan에 추가를 해주어야 한다.

 

1) DTO 역할을 수행할 Item 클래스

=> 필드의 자료형이 중요 -  자료형이 안맞으면 예외가 발생

패키지이름/domain에 생성

테이블에서 자료형 컬럼이름 잘 확인하고 작성한 후 getters & setters하고 toString()까지 해준다.

lombok을 사용해도 된다.

 

2) Item 테이블에 수행할 SQL을 저장하는 mapper

=>src/main/resources 디렉토리에 mybatis 디렉토리를 생성하고 그 안에 mappers디렉토리를 생성하고 item.xml

 

3) root-context.xml 파일에 데이터베이스 접속과 MyBatis 연동 객체를 생성하는 코드작성

 

	<!-- XML을 이용한 MyBatis 사용 설정, 일기만 한다면 여기서 끝내도 된다 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<!-- mappers 디렉토리 안에 있는 모든 xml을 mapper 로 사용, 다른 용도의 xml 파일이 있으면 에러 -->
		<property name="mapperLocations" value="classpath:mappers/**/*.xml" />
	</bean>
	
	<!-- xml을 이용해서 MyBatis를 사용할 때 생성 -->
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory" />
	</bean>
	
	<!-- 인터페이스를 이용해서 MyBatis를 이용할 때 생성 -->
	<bean id="goodMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="sqlSessionFactory" ref="sqlSessionFactory" />
		<property name="mapperInter

 

4) servlet-context.xml 파일에 트랜잭션 사용을 설정

 

	<!-- MyBatis 트랜잭션 관련 bean -->
	<beans:bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</beans:bean>

 

 

5) ItemDAO 클래스를 생성하고 메소드 작성

 

@Repository
public class ItemDAO {

	@Autowired
	private SqlSession sqlSession;

	//데이터 번호를 받아서 번호에 해당하는 데이터를 가져오는 메소드
	public List<Item> list(Map map){
		return sqlSession.selectList("item.list", map);
	}

	//itemid를 받아서 1개의 데이터 가져오는 메소드
	public Item detail(Integer itemid){
		return sqlSession.selectOne("item.detail", itemid);

	}

}

 

=>어노테이션 생략하면 안되고 sql 이름을 정확하게 기재해야 합니다.

 

6) ItemService 인터페이스를 만들고 메소드를 선언

 

 

public interface ItemService {

	public void list(HttpServletRequest request);

	public void detail(HttpServletRequest request);

}

 

 

7) ItemServiceImpl 클래스를 만들고 메소드를 구현

 

@Service
public class ItemServiceImpl implements ItemService {
	@Autowired
	private ItemDAO itemDao;

	@Override
	public void list(HttpServletRequest request) {
	//매개변수로 페이지 번호가 온다고 가정
		String pageno = request.getParameter("pageno");

		//페이지 당 데이터 개수
		int cnt = 2;

		//시작번호 와 끝나는 번호 생성
		int start = Integer.parseInt(pageno) * cnt - (cnt-1);
		int end = Integer.parseInt(pageno) * cnt;

		//DAO 의 파라미터 만들기
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("start", start);
		map.put("end", end);

		//DAO 의 메소드를 실행하고 결과를 저장
		List<Item> list = itemDao.list(map);
			request.setAttribute("list", list);
	}

	@Override
	public void detail(HttpServletRequest request) {
    	String itemid = request.getParameter("itemid");
			Item item = itemDao.detail(Integer.parseInt(itemid));
			request.setAttribute("item", item);
	}

}