**Oracle & MyBatis를 이용한 Spring MVC Project 와 Android 통신
=> 데이터 조회
1. Oracle에 접속해서 샘플 데이터 생성
1) 접속 정보
=> URL : 192.168.0.76
=> PORT : 1521
=> ID : user08
=> PW : user08
2) SQL 샘플
-- 혹시 있을지 모르는 테이블 제거
drop table item;
-- ITEM 테이블 생성
CREATE TABLE ITEM(
itemid number(5),
itemname VARCHAR2(100),
price number(6),
description VARCHAR2(200),
pictureurl VARCHAR2(100),
PRIMARY KEY (itemid)
);
-- ITEM TABLE에 샘플 데이터 입력
insert into item values(1, 'Lemon', 500, 'Vitamin-A', 'lemon.jpg');
insert into item values(2, 'Orange', 1500, 'Vitamin-B', 'orange.jpg');
insert into item values(3, 'Kiwi', 2000, 'Vitamin-C', 'kiwi.jpg');
insert into item values(4, 'Grape', 1000, 'Vitamin-D', 'grape.jpg');
insert into item values(5, 'Strawberry', 2000, 'Vitamin-E', 'strawberry.jpg');
insert into item values(6, 'Mandarin', 300, 'Vitamin-F', 'mandarin.jpg');
commit;
-- 생성 데이터 확인
select * from ITEM;
-— itemid 별로 내림차순 정렬한 한 후 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 버전을 변경
<properties>
<java-version>1.8</java-version>
<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
=>여기까지 저장하고 실행합니다.
2) 에러발생 이유
=> 포트 충돌
- 다른 톰캣이 실행중인지 확인
- 오라클 설치 중이면 servers - server.xml 파일의 port가 8080이면 다른 번호로 변경
- port 번호가 -1인게 있으면 8005번으로 수정
3) Oracle을 사용하는 경우 repositories를 설정
=> repositories는 중앙저장소가 아닌 곳에서 다운로드 받을 때 설정
=> Oracle은 Maven 중앙 저장소에 없습니다.
<repositories>
<repository>
<id>oracle</id>
<name>ORACLE JDBC Repository</name>
http://maven.jahia.org/maven2
</repository>
</repositories>
4) 필요 의존성을 dependencies 안에 주입
=> oracle, mysql, spring-jdbc, spring-orm, mybatis, mybatis-spring, hibernate : 데이터베이스 연동 필수 라이브러리
=> json 라이브러리 : json 출력
=> spring-test : junit을 이용해서 spring project 테스트
=> commons-fileupload : 파일 업로드
=> jbcrypt : 복호화가 불가능한 암호화
=> lombok
<!-- 오라클 -->
<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 : Item
=> 테이블에 수행할 SQL을 저장하는 mapper(mybatis) : mybatis/mappers/item.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 클래스
=> 필드의 자료형이 중요 - 자료형이 안맞으면 예외가 발생
package com.naver.insa8029.domain;
public class Item {
private int itemid;
private String itemname;
private int price;
private String description;
private String pictureurl;
public Item(int itemid, String itemname, int price, String description, String pictureurl) {
super();
this.itemid = itemid;
this.itemname = itemname;
this.price = price;
this.description = description;
this.pictureurl = pictureurl;
}
public Item() {
super();
// TODO Auto-generated constructor stub
}
public int getItemid() {
return itemid;
}
public void setItemid(int itemid) {
this.itemid = itemid;
}
public String getItemname() {
return itemname;
}
public void setItemname(String itemname) {
this.itemname = itemname;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPictureurl() {
return pictureurl;
}
public void setPictureurl(String pictureurl) {
this.pictureurl = pictureurl;
}
@Override
public String toString() {
return "Itme [itemid=" + itemid + ", itemname=" + itemname + ", price=" + price + ", description=" + description
+ ", pictureurl=" + pictureurl + "]";
}
}
2) Item 테이블에 수행할 SQL을 저장하는 mapper 파일을 생성
=> src/main/resources 디렉토리에 mybatis 디렉토리를 생성하고 그 안에 mappers 디렉토리를 생성하고 item.xml 로 생성
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="item">
<!-- 출력할 데이터번호를 받아서 item 테이블에서
itemid의 내림차순 정렬을 수행할 데이터 가져오기 -->
<select id="list" parameterType="java.util.Map"
resultType="com.naver.insa8029.domain.Item">
select itemid,itemname,price,description,pictureurl
from (select rownum rnum, itemid, itemname, price, description, pictureurl
from(select * from item order by itemid desc))
where rnum >= #{start} and rnum <= #{end}
</select>
<!-- 상세보기를 위한 메소드 -->
<select id="detail" parameterType="java.lang.Integer"
resultType="com.naver.insa8029.domain.Item">
select itemid,itemname,price,description,pictureurl
from item
where itemid = #{itemid}
</select>
</mapper>
3) root-context.xml 파일에 데이터베이스 접속과 MyBatis 연동 객체를 생성하는 코드를 작성
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 데이터 베이스 접속 정보 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>oracle.jdbc.driver.OracleDriver</value>
</property>
<property name="url">
<value>jdbc:oracle:thin:@192.168.0.76:1521:xe</value>
</property>
<property name="username">
<value>user08</value>
</property>
<property name="password">
<value>user08</value>
</property>
</bean>
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations"
value="classpath:mybatis/mappers/**/*.xml" />
</bean>
<bean id="sqlSession"
class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
</beans>
4) servlet-context.xml 파일에 트랜잭션 사용을 설정
=> tx 네임 스페이스를 추가
<!-- MyBatis 트랜잰견 관련 bean -->
<beans:bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Controller가 처리하지 못하는 요청은 WAS가 처리하도록 설정 -->
<default-servlet-handler/>
5) ItemDAO 클래스를 생성하고 메소드 작성
package com.naver.insa8029.dao;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.naver.insa8029.domain.Item;
@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);
}
}
=> 어노테이션 생략X, sql 이름을 정확하게 기재
6) ItemService 인터페이스를 만들고 메소드를 선언
package com.naver.insa8029.service;
import javax.servlet.http.HttpServletRequest;
public interface ItemService {
public void list(HttpServletRequest request);
public void detail(HttpServletRequest request);
}
7) ItemServiceImpl 클래스를 만들고 메소드를 구현
package com.naver.insa8029.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.naver.insa8029.dao.ItemDAO;
import com.naver.insa8029.domain.Item;
@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);
}
}
8) json으로 출력하는 Controller를 생성하고 메소드를 작성
=> ItemDataController
package com.naver.insa8029.oracleserver;
import java.util.HashMap;
import java.util.List;
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.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.naver.insa8029.domain.Item;
import com.naver.insa8029.service.ItemService;
@RestController
public class ItemDataController {
@Autowired
private ItemService itemService;
@RequestMapping(value="list", method=RequestMethod.GET)
public Map<String, Object> list(HttpServletRequest request){
//서비스의 메소드를 실행
itemService.list(request);
Map<String, Object> map = new HashMap<String, Object>();
//데이터를 가져와서 map에 출력
List<Item> list = (List<Item>)request.getAttribute("list");
map.put("list", list);
return map;
}
@RequestMapping(value = "detail", method = RequestMethod.GET)
public Map<String, Object> detail(HttpServletRequest request) {
itemService.detail(request);
Item item = (Item) request.getAttribute("item");
Map<String, Object> map = new HashMap<String, Object>();
map.put("item", item);
return map;
}
}
**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자료형(O) : 0번째 데이터를 자료형으로 가져오는 것
- 객체.get자료형("속성명") : 속성명에 해당하는 데이터를 자료형으로 가져옴
String data = {“list":[{"itemid":6,"itemname":"Mandarin","price":300,"description":"Vitamin-F","pictureurl":"mandarin.jpg"},{"itemid":5,"itemname":"Strawberry","price":2000,"description":"Vitamin-E","pictureurl":"strawberry.jpg"}]}
=>item 와 itemname 만 가져와서 출력하기
JSONObject 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=페이지번호
{"list":[{"itemid":6,"itemname":"Mandarin","price":300,"description":"Vitamin-F","pictureurl":"mandarin.jpg"},{"itemid":5,"itemname":"Strawberry","price":2000,"description":"Vitamin-E","pictureurl":"strawberry.jpg"}]}
=>http://192.168.0.200:8080/oracleserver/detail?itemid=1
{"item":{"itemid":1,"itemname":"Lemon","price":500,"description":"Vitamin-A","pictureurl":"lemon.jpg"}}
1. Android Application Project를 생성
2. 인터넷 사용 권한을 설정 - AndroidManifest.xml 파일에서 작업
=> 네트워크를 사용할 때는 INTERNET 권한이 있어야 함
=> 웹 서버에 접속할 때 http 프로토콜을 사용하는 경우, Application에 android:usesCleartextTraffic="true” 설정
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:usesCleartextTraffic="true"
=> 버튼을 누르면 첫번째 페이지 데이터를 가져와서 텍스트 뷰를 출력하고 다시 누르면 다음페이지의 데이터를 추가
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"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="데이터 가져오기"
android:id="@+id/btn"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/display"/>
</LinearLayout>
4. MainActivity.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 Button btn;
private TextView display;
//페이지 번호를 저장할 변수
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.68:8080/insa8029/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 +=1){
//배열에서 i번째 데이터 가져오기
JSONObject item = list.getJSONObject(i);
//itemid와 itemname을 가져와서 msg에 추가
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);
btn = (Button)findViewById(R.id.btn);
display = (TextView)findViewById(R.id.display);
//스레드를 생성하여 데이터를 출력
ThreadEx th = new ThreadEx();
th.start();
//버튼 클릭시 다음 페이지 데이터 추가
btn.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View view) {
pageno += 1;
ThreadEx th = new ThreadEx();
th.start();
}
});
}
}
5. 실행가능한 Activity 추가(DetailActivity)
6. activity_layout.xml 파일에 화면 디자인
=> TextView 1개와 ImageView1개를 배치
=> 아이템 이름과 이미지를 출력
<?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"
android:orientation="vertical"
tools:context=".DetailActivity">
<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가 처리하지 못하는 요청은 WAS가 처리하도록 설정 -->
<default-servlet-handler/>
8. DetailActivity.java 파일에 코드 작성
2) Member 테이블의 SQL작업을 위한 mapper 파일을 추가
=> src/main/resources/mybatis/mappers/member.xml
'수업 정리' 카테고리의 다른 글
80일차 수업정리(XML Parsing, Adapter) (0) | 2020.07.29 |
---|---|
76~78일차 수업정리(MySQL&Hibernate Project) (0) | 2020.07.23 |
75일차 수업정리(Android - Socket) (0) | 2020.07.22 |
74일차 수업 정리(Android - Thread) (0) | 2020.07.21 |
73일차 수업 정리(Android - 파일 입출력, SQLite) (0) | 2020.07.20 |