Android App: https://github.com/itggangpae/AndroidPortfolio.git
MySQL Server: https://github.com/itggangpae/mysqlhibernate.git
**list
1. detail : itemid를 1개 파라미터로 전달
2. insert : post 방식으로 처리, 파일 전달
1) itemid : maxid를 이용해서 생성
2) Itemname, price, description: 입력
3) pictureurl은 카메라를 아직 사용하지 못하므로 raw 디렉토리에 저장하고 사용
**Oracle과 MyBatis
=> 공통점 : 관계형 데이터베이스(테이블의 집합으로 데이터 표시)
=> 사용하는 기업의 규모가 다름
- MySQL : 중견, 중소, 호스팅하는 곳에서 많이 사용 / 한글 사용을 위해서는 인코딩이 필요
- Oracle : 대기업, 관공서에서 많이 사용 / 한글사용을 위한 인코딩이 필요없음
**MyBatis와 Hibernate
1. MyBatis
=> SQL Mapper Framework
=> Java와 SQL 코드의 분리
=> 배우기가 쉬워 SI분야에서 주로 사용
2. Hibernate
=> ORM(Object Relation Mapper) Framework
=> Java 객체와 테이블의 하나의 행을 매핑
=> 배우기는 어려우나 성능이 우수하여 솔루션 분야세어 주로 사용
**Sping Maven MVC Project
1. Maven
=> 프로젝트 관리 도구이면서 빌드 도구
=> pom.xml 파일을 이용하여 프로젝트를 관리하고 빌드를 수행
2. Gradle
=> 빌드 도구
=> Android Studio의 빌드 도구가 Gradle
=> build.gradle이라는 파일을 이용하여 빌드를 수행
3. Jenkins
=> 빌드 도구
=> 현업에서 가장 많이 사용하는 빌드 도구
4. MVC Project
=> 기본적인 웹 설정이 되어있는 프로젝트
**작업 내용
- item 테이블의 검색 및 페이징, 상세보기, 데이터 삽입
- member 테이블의 데이터 삽입, 로그인, 정보 수정, 탈퇴
=> 삽입, 로그인, 수정, 탈퇴는 POST 방식으로 처리
**MySQL 접속 정보
- 192.168.0.76 : 3306
- user08 : user08 : 데이터베이스 이름, 아이디, 비밀번호
**SQL 테이블 생성 및 샘플데이터 삽입
1. 테이블 및 샘플 데이터 생성
drop table item;
CREATE TABLE ITEM(
itemid int,
itemname VARCHAR(100),
price int,
description VARCHAR(200),
pictureurl VARCHAR(100),
PRIMARY KEY (itemid)
)engine=InnoDB DEFAULT CHARSET=utf8;
insert into item values(1, '레몬', 500, 'Vitamin-A', 'lemon.jpg');
insert into item values(2, '오렌지', 1500, 'Vitamin-B', 'orange.jpg');
insert into item values(3, '키위', 2000, 'Vitamin-C', 'kiwi.jpg');
insert into item values(4, '포도', 1000, 'Vitamin-D', 'grape.jpg');
insert into item values(5, '딸기', 2000, 'Vitamin-E', 'strawberry.jpg');
insert into item values(6, '감귤', 300, 'Vitamin-F', 'mandarin.jpg');
insert into item values(7, '오징어', 3300, 'Protein', 'mandarin.jpg');
commit;
select * from ITEM;
-- itemname에 오가 포함된 데이터 앞에서 3개 가져오기
select *
from item
where itemname like '%오%'
LIMIT 0, 3;
-- 회원테이블
CREATE TABLE MEMBER(
email varchar(200) PRIMARY KEY,
nickname varchar(200) unique,
pw varchar(100) NOT NULL,
profile varchar(100)
)engine=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO member(email, nickname, pw, profile)
values('insa8029@naver.com', '피리리릭', '1234', null);
SELECT * FROM MEMBER;
**Item 테이블 서버 작업을 위한 Spring MVC Project
=> 아이템 테이블에 검색어를 이용하여 검색한 결과를 출력하는 작업
=> 아이템 테이블에 기본키를 이용하여 하나의 데이터를 조회하여 출력하는 작업
=> 아이템 테이블에 데이터를 삽입하는 작업
1. Spring MVC Project 생성
=> MySQLServer : com.naver.insa8029
2. pon.xml 파일에 가서 기본 라이브러리들의 버전을 변경
=> 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>
=> 프로젝트를 선택하고 마우슨 오른쪽을 클릭하여 [Maven]-[Update Project]를 실행
3. pom.xml 파일에 필요한 의존성 라이브러리를 설정
1) 오라클을 위한 repository 추가
<repositories>
<repository>
<id>oracle</id>
<name>ORACLE JDBC Repository</name>
<url>http://maven.jahia.org/maven2</url>
</repository>
</repositories>
2) dependencies 에 필요한 의존성을 추가
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- 오라클 -->
<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>
3) pom.xml 파일을 저장하고 실행
4. Lombok 설치
=> 터미널에 입력 : java -jar 파일경로
5. web.xml 파일에 파라미터 인코딩 설정 필터 추가
<!-- 파라미터 인코딩 필터 설정 -->
<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>
=>저장하고 재실행
6. servlet-context.xml 파일에 파일 업로드와 Controller가 처리하지 못하는 요청은 WAS가 처리해주도록 하는 설정을 추가
<default-servlet-handler/>
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</beans:bean>
7. domain 패키지에 Item 테이블과 연동할 DTO 클래스 생성 : 자료형을 정확하게 설정
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 "Item [itemid=" + itemid + ", itemname=" + itemname + ", price=" + price + ", description=" + description
+ ", pictureurl=" + pictureurl + "]";
}
}
8. dao 패키지에 Item 클래스 와 Item 테이블을 매핑하는 item.hbm.xml 파일을 생성하고 설정
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="kakao.itggangpae.mysqlserver.domain">
<class name="Item" table="Item">
<id name="itemid" column="itemid"/>
<property name="itemname" column="itemname"/>
<property name="price" column="price"/>
<property name="description" column="description"/>
<property name="pictureurl" column="pictureurl"/>
</class>
</hibernate-mapping>
9. ItemDAO 클래스를 생성하고 필요한 메소드를 생성
//dao
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import kakao.itggangpae.mysqlserver.domain.Item;
@Repository
public class ItemDAO {
@Autowired
private SessionFactory sessionFactory;
//검색항목과 검색어 그리고 페이지 번호를 이용해서 해당하는
//데이터를 목록으로 리턴하는 메소드
//페이징을 할 때 오라클은 시작번호 와 종료번호를 알아야 하지만
//MySQL은 시작번호와 데이터 개수를 알아야 합니다.
//오라클은 1부터 시작하지만 MySQL은 0부터 시작합니다.
//검색항목과 검색어 그리고 시작번호 페이지당 데이터 개수를
//하나의 Map으로 묶어서 사용
public List<Item> list(Map<String, Object> map){
//결과를 저장할 list
List<Item> list = new ArrayList<Item>();
//검색항목 - searchtype, 검색어 - value
//시작번호 - start 페이지 당 데이터 개수 - size
//파라미터 읽기 - ServiceImpl에서 만들어서 넘겨주어야 합니다.
String searchtype = (String)map.get("searchtype");
String value = (String)map.get("value");
int start = (Integer)map.get("start");
int size = (Integer)map.get("size");
if(value != null) {
//like 검색을 하기 위해서 % 사용
//대소문자 구분하지 않고 검색하기 위해서 toLowerCase 사용
value = "%" + value.toLowerCase() + "%";
}
//검색 항목이 없는 경우
//itemname에서 검색, description에서 검색
//itemname 과 descritpion에서 검색
if(searchtype == null) {
list = sessionFactory.getCurrentSession()
.createNativeQuery(
"select * from item limit "
+ start + "," + size ).getResultList();
}else if(searchtype.equals("itemname")) {
list = sessionFactory.getCurrentSession()
.createNativeQuery(
"select * from item where "
+ "lower(itemname) like \'"
+ value + "\' limit " +
start + "," + size).getResultList();
}else if(searchtype.equals("description")) {
list = sessionFactory.getCurrentSession()
.createNativeQuery(
"select * from item where "
+ "lower(description) like \'"
+ value + "\' limit " +
start + "," + size).getResultList();
}else if(searchtype.equals("both")) {
list = sessionFactory.getCurrentSession()
.createNativeQuery(
"select * from item where "
+ "lower(description) like \'"
+ value + "\'" +
" or lower(itemname) like \'" +
value + "\' limit " + start +
", " + size).getResultList();
}
return list;
}
//검색 결과의 데이터 개수를 리턴하는 메소드
//하이버네이트에서는 정수를 리턴할 때는 BigInteger로 리턴됩니다.
public BigInteger count(Map<String, Object> map) {
//파라미터 읽기 - ServiceImpl에서 만들어서 넘겨주어야 합니다.
String searchtype = (String)map.get("searchtype");
String value = (String)map.get("value");
List<BigInteger> list = null;
if(value != null) {
value = "%" + value.toLowerCase() + "%";
}
if(searchtype == null) {
list = sessionFactory.getCurrentSession()
.createNativeQuery(
"select count(*) from item")
.getResultList();
}else if(searchtype.equals("itemname")) {
list = sessionFactory.getCurrentSession()
.createNativeQuery(
"select count(*) from item " +
"where lower(itemname) like \'" +
value + "\'")
.getResultList();
}else if(searchtype.equals("description")) {
list = sessionFactory.getCurrentSession()
.createNativeQuery(
"select count(*) from item " +
"where lower(description) like \'" +
value + "\'")
.getResultList();
}else if(searchtype.equals("both")) {
list = sessionFactory.getCurrentSession()
.createNativeQuery(
"select count(*) from item " +
"where lower(description) like \'" +
value + "\'" +
" or lower(itemname) like \'" +
value + "\'")
.getResultList();
}
return list.get(0);
}
//itemid를 가지고 데이터 1개를 찾아오는 메소드
public Item detail(int itemid) {
Item item =
sessionFactory.getCurrentSession()
.get(Item.class, itemid);
return item;
}
//가장 큰 ItemId를 찾아오는 메소드
public int maxid() {
List<Integer> list =
sessionFactory.getCurrentSession()
.createNativeQuery(
"select max(itemid) from item")
.getResultList();
return list.get(0);
}
//데이터를 삽입하는 메소드
public void insert(Item item) {
sessionFactory.getCurrentSession().save(item);
}
}
10. root-context.xml 파일에 연결할 데이터베이스 설정과 하이버네이트 설정을 추가
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://192.168.0.76:3306/user00?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="user00"/>
<property name="password" value="user00"/>
</bean>
<!-- 하이버네이트 설정 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>
kakao/itggangpae/mysqlserver/dao/item.hbm.xml
</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
</value>
</property>
</bean>
11. Servlet-context.xml 파일에 하이버네이트 트랜잭션 사용을 위한 설정을 추가
=> 하이버네이트는 트랜잭션 사용이 필수
=> 트랜잭션의 적용은 Service
1) servlet-context.xml 파일에 tx 네임스페이스를 추가
2) bean 설정
<beans:bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<beans:property name="sessionFactory"
ref="sessionFactory"/>
</beans:bean>
<tx:annotation-driven/>
12. service 패키지에 ItemService 인터페이스를 생성하고 필요한 메소드 선언
public interface ItemService {
//검색어를 가지고 검색하는 메소드
public void list(HttpServletRequest request);
//itemid를 이용해서 하나의 데이터를 가져오는 메소드
public void detail(HttpServletRequest request);
//데이터를 삽입하는 메소드
//파일을 업로드 할 때는 일반 HttpServletRequest로 못함
public void insert(MultipartHttpServletRequest request);
}
13. service 패키지에 ItemService 인터페이스를 implements 한 ItemServiceImpl 클래스를 만들고 메소드를 구현
//service
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import kakao.itggangpae.mysqlserver.dao.ItemDAO;
import kakao.itggangpae.mysqlserver.domain.Item;
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemDAO itemDao;
@Override
@Transactional
public void list(HttpServletRequest request) {
//파라미터 읽기
String searchtype = request.getParameter("searchtype");
String value = request.getParameter("value");
String pageno = request.getParameter("pageno");
//작업을 수행
//한 페이지에 보여질 데이터 개수
int size = 3;
//시작 위치 번호를 저장할 변수
//MySQL은 데이터 번호가 0부터 시작합니다.
int start = 0;
if(pageno != null) {
start = (Integer.parseInt(pageno) - 1) * size;
}
//DAO 메소드의 파라미터를 만들기
Map<String, Object> map = new HashMap<String, Object>();
map.put("searchtype", searchtype);
map.put("value", value);
map.put("start", start);
map.put("size", size);
//DAO 메소드를 호출해서 결과를 저장
List<Item> list = itemDao.list(map);
request.setAttribute("list", list);
int count = itemDao.count(map).intValue();
request.setAttribute("count", count);
}
@Override
@Transactional
public void detail(HttpServletRequest request) {
String itemid = request.getParameter("itemid");
Item item = itemDao.detail(
Integer.parseInt(itemid));
request.setAttribute("item", item);
}
@Override
@Transactional
public void insert(MultipartHttpServletRequest request) {
//itemid, itemname, price, description
//pictureurl을 만들어서 데이터를 삽입
int itemid = 1;
//데이터 개수 가져오기
int count = itemDao.count(new HashMap<String, Object>()).intValue();
//데이터가 존재하면 가장 큰 itemid의 값에 +1
if(count != 0) {
itemid = itemDao.maxid() + 1;
}
String itemname = request.getParameter("itemname");
int price = Integer.parseInt(request.getParameter("price"));
String description = request.getParameter("description");
//파일의 기본값을 설정
String pictureurl = "default.jpg";
//파일 파라미터 가져오기
MultipartFile image = request.getFile("pictureurl");
//전송된 파일이 존재하면
if(image != null && image.isEmpty() == false) {
//파일을 저장할 디렉토리 경로 가져오기
String filePath = request.getRealPath("/img");
//새로운 파일명 만들기
pictureurl = UUID.randomUUID() + image.getOriginalFilename();
//실제 파일 경로 만들기
filePath = filePath + "/" + pictureurl;
try {
//파일을 기록할 출력 스트림 생성
FileOutputStream fos = new FileOutputStream(filePath);
//파일 업로드
fos.write(image.getBytes());
fos.close();
}catch(Exception e) {
System.out.println("파일 저장 예외:" + e.getMessage());
}
Item item = new Item();
item.setItemid(itemid);
item.setItemname(itemname);
item.setPrice(price);
item.setDescription(description);
item.setPictureurl(pictureurl);
itemDao.insert(item);
request.setAttribute("insert", true);
}
}
}
14. 기본 패키지에 Item 테이블의 데이터를 전달하는 Controller 클래스를 생성하고 메소드 작성
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 org.springframework.web.multipart.MultipartHttpServletRequest;
import kakao.itggangpae.mysqlserver.domain.Item;
import kakao.itggangpae.mysqlserver.service.ItemService;
@RestController
public class ItemDataController {
@Autowired
private ItemService itemService;
//검색해서 데이터를 전송하는 요청을 생성
@RequestMapping(value="list")
public Map<String, Object> list(
HttpServletRequest request){
//서비스 메소드를 호출해서 결과를 가져옵니다.
itemService.list(request);
List<Item> list =
(List<Item>)request.getAttribute("list");
int count =
(Integer)request.getAttribute("count");
//가져온 데이터를 출력할 Map에 저장하고 Map을 출력
Map<String, Object> map =
new HashMap<String, Object>();
map.put("count", count);
map.put("list", list);
return map;
}
@RequestMapping(value="detail")
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;
}
//데이터를 입력받아서 수행하는 처리는 method를 POST로 설정
@RequestMapping(value="insert",
method=RequestMethod.POST)
public Map<String, Object> insert(
MultipartHttpServletRequest request){
itemService.insert(request);
Boolean result =
(Boolean)request.getAttribute(“insert”);
Map<String, Object> map =
new HashMap<String, Object>();
//삽입에 성공하면 result는 true 그렇지 않으면 false
if(result != null)
map.put("result", result);
else
map.put("result", false);
return map;
}
}
15. 서버를 실행하고 테스트
=> http://localhost:8080/mysqlserver/list
=> http://localhost:8080/mysqlserver/list?searchtype=itemname&value=%EB%A0%88&pageno=1
=> http://localhost:8080/mysqlserver/detail?itemid=2
**Item 테이블의 데이터를 사용하는 Android App 생성
1. Android Application을 생성
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=".MainActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Spinner
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:id="@+id/searchtype"/>
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:id="@+id/value"
android:hint="검색어 입력"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="검색"
android:id="@+id/btnsearch"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="다음"
android:id="@+id/btnnext"/>
</LinearLayout>
<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/list"/>
</ScrollView>
</LinearLayout>
3. res/values 디렉토리에 array.xml 파일을 생성하고 작성
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="searchtype_array">
<item>--</item>
<item>이름</item>
<item>설명</item>
<item>이름 또는 설명</item>
</string-array>
</resources>
4. MainActivity 작업
1) 필요한 인스턴스 변수를 선언
//콤보 박스 역할을 하는 위젯
private Spinner searchtype;
private EditText value;
private Button btnsearch, btnnext;
private TextView list;
//Spinner에 데이터를 연결할 Adapter
private ArrayAdapter<CharSequence> adapter;
//페이지 번호와 페이지 당 데이터 개수를 저장할 변수
int pageNo = 1;
int size = 3;
//조건에 맞는 데이터 개수를 저장할 변수
int cnt;
2) onCreate 메소드에서 View를 전부 찾아서 대입
searchtype = (Spinner)findViewById(R.id.searchtype);
adapter = ArrayAdapter.createFromResource(
this,R.array.searchtype_array,
android.R.layout.simple_spinner_dropdown_item
);
searchtype.setAdapter(adapter);
value = (EditText)findViewById(R.id.value);
btnnext = (Button)findViewById(R.id.btnnext);
btnsearch = (Button)findViewById(R.id.btnsearch);
list = (TextView)findViewById(R.id.list);
3) 다운로드 받은 결과를 화면에 출력하기 위한 핸들러를 생성
//출력할 내용
String result = "";
Handler handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message message){
list.setText(result);
}
};
4) 데이터를 다운로드 받아서 파싱할 스레드 클래스를 생성
//데이터를 다운로드 받아서 파싱하는 스레드
class ThreadEx extends Thread{
//다운로드 받은 문자열을 저장할 객체
StringBuilder sb = new StringBuilder();
public void run(){
try{
URL url = null;
//콤보 박스 선택한 항목 번호를 idx에 저장
int idx = searchtype.getSelectedItemPosition();
if(idx == 0){
url = new URL(
"http://192.168.0.200:8080/mysqlserver/list?" +
"pageno=" + pageNo);
}else if(idx == 1){
url = new URL(
"http://192.168.0.200:8080/mysqlserver/list?"
+ "searchtype=itemname&" + "value=" +
value.getText().toString() + "&pageno="
+pageNo
);
}else if(idx == 2){
url = new URL(
"http://192.168.0.200:8080/mysqlserver/list?"
+ "searchtype=description&" + "value=" +
value.getText().toString() + "&pageno="
+pageNo
);
}else{
url = new URL(
"http://192.168.0.200:8080/mysqlserver/list?"
+ "searchtype=both&" + "value=" +
value.getText().toString() + "&pageno="
+pageNo
);
}
HttpURLConnection con = (
HttpURLConnection)url.openConnection();
BufferedReader br = new BufferedReader(
new InputStreamReader(con.getInputStream()));
while(true){
String line = br.readLine();
if(line == null){
break;
}
sb.append(line + "\n");
}
br.close();
con.disconnect();
}catch(Exception e){
Log.e("다운로드 예외", e.getMessage());
}
try{
//객체로 변환
JSONObject object = new JSONObject(sb.toString());
//데이터 개수는 count에 숫자로 저장
cnt = object.getInt("count");
//list 키의 데이터를 배열로 가져오기
JSONArray ar = object.getJSONArray("list");
for(int i=0; i<ar.length(); i=i+1){
JSONArray temp = ar.getJSONArray(i);
result = result + temp.getString(1) + "\n";
}
//핸들러에게 출력을 요청
handler.sendEmptyMessage(0);
}catch(Exception e){
Log.e("파싱에러", e.getMessage());
}
}
}
5) Activity가 활성화 될 때 마다 호출되는 메소드를 재정의
@Override
public void onResume(){
super.onResume();
new ThreadEx().start();
}
5. 안드로이드에서 네트워크 기능을 사용하기
1) AndroidManifest.xml 파일에서 INTERNET 권한을 부여
2) 접속할 곳의 URL이 http 이면 application 태그에 android:usesCleartextTraffic="true"를 추가
6. MainActivity.java 파일의 onCreate에서 버튼을 클릭했을 때 호출할 이벤트 핸들러를 작성
btnnext.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
pageNo = pageNo + 1;
new ThreadEx().start();
}
});
btnsearch.setOnClickListener(new Button.OnClickListener(){
public void onClick(View v){
pageNo = 1;
result = "";
new ThreadEx().start();
}
});
**안드로이드 상세보기를 위해 알아야 할 것
=> itemid를 넘겨받아서 detail에 요청을 전송하여 상세보기를 구현(itemid를 1로 가정)
=> image도 다운로드 받아서 구현
=> 웹에서 텍스트와 이미지를 다운로드 받아서 출력하는 경우 하나의 스레드에서 구현 가능하지만 대부분의 경우 일단 텍스트를 먼저 다운로드 받아 출력 후, 이미지는 별도의 스레드에서 다운로드 받아서 출력하는 것을 권장
=> 안드로이드에서 서버와 연동시 주의점
1) INTERNET 권한 부여
2) http 서버와 연동시 별도의 설정 필요
3) Android에서는 네트워크 사용 코드는 반드시 스레드에 작성해야 함
4) Android에서는 메인 스레드를 제외하고는 출력하는 코드를 작성할 수 없음
- 스레드를 사용해서 데이터를 다운 받은 후, UI를 갱신할 경우 Handler를 이용
=> Thread와 Handler로 나누어서 작성하는 것이 싫으면 AsyncTask를 사용하면 됨
(AsyncTask는 메소드로 Thread의 기능과 Handler의 기능을 분할해 놓은 클래스)
**상세보기 구현
1. Server에 img 디렉토리가 있는지 확인하고 없으면 img 디렉토리를 생성하고 기본 이미지 삽입
2. Android project에 실행가능한 Activity 추가(ItemDetailActivity)
3. 레이아웃 파일에 화면 디자인
=> 상단에 itemname을 출력
=> 설명 : description 을 출력
=> 가격 : price를 출력
=> pictureurl에 해당하는 이미지를 출력
<?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=".ItemDetailActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="32sp"
android:id="@+id/lblitemname"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="설명"
android:textSize="32dp"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:textSize="32dp"
android:id="@+id/lbldescription"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="가격"
android:textSize="32dp"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:textSize="32dp"
android:id="@+id/lblprice"/>
</LinearLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/imgpictureurl"/>
</LinearLayout>
4. DetailActivity.java 파일 작성
public class ItemDetailActivity extends AppCompatActivity {
//화면에 사용할 뷰
TextView lblitemname, lbldescription, lblprice;
ImageView imgpictureurl;
//텍스트 데이터를 웹에서 다운로드 받아서 출력
//다운로드 -> 파싱 -> 출력
//텍스트 데이터를 출력할 핸들러
Handler textHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message message){
//넘어온 데이터 찾아오기
Map<String,Object> map =
(Map<String,Object>)message.obj;
//데이터 출력하기
lblitemname.setText((String)map.get("itemname"));
lblprice.setText((Integer)map.get("price") +"");
lbldescription.setText((String)map.get("description"));
//이미지 파일명을 ImageThread에게 넘겨서 출력
new ImageThread((String)map.get("pictureurl")).start();
}
};
//텍스트 데이터를 가져올 스레드 클래스
class TextThread extends Thread{
StringBuilder sb = new StringBuilder();
@Override
public void run(){
//텍스트 데이터 다운로드
try{
//URL 만들
URL url = new URL(
"http://192.168.0.200:8080/mysqlserver/detail?itemid=" + 1);
//Connection 객체 만들기
HttpURLConnection con =
(HttpURLConnection)url.openConnection();
//옵션 설정
con.setUseCaches(false);
con.setConnectTimeout(30000);
//스트림 객체 생성
BufferedReader br = new BufferedReader(
new InputStreamReader(
con.getInputStream()));
//문자열 읽기
while(true){
String line = br.readLine();
if(line == null){
break;
}
sb.append(line + "\n");
}
br.close();
con.disconnect();
}catch(Exception e){
//이 메시지가 보이면 서버가 구동 중인지 확인하고
//URL은 제대로 입력했는지 확인
Log.e("다운로드 에러", e.getMessage());
}
Log.e("다운로드 받은 문자열", sb.toString());
try{
//다운로드 받은 문자열에서 필요한 데이터 추출
JSONObject object = new JSONObject(sb.toString());
JSONObject item = object.getJSONObject("item");
String itemname = item.getString("itemname");
int price = item.getInt("price");
String descritpion = item.getString("description");
String pictureurl = item.getString("pictureurl");
//4개의 데이터를 하나로 묶기
Map<String, Object> map =
new HashMap<>();
map.put("itemname", itemname);
map.put("price", price);
map.put("description", descritpion);
map.put("pictureurl", pictureurl);
//핸들러에게 데이터를 전송하고 호출
Message message = new Message();
message.obj = map;
textHandler.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;
imgpictureurl.setImageBitmap(bitmap);
}
};
//이미지 다운로드를 위한 스레드
class ImageThread extends Thread{
//다운로드 받을 파일명
String pictureurl;
public ImageThread(String pictureurl){
this.pictureurl = pictureurl;
}
@Override
public void run(){
try{
URL url = new URL("http://192.168.0.200:8080/mysqlserver/img/" + pictureurl);
HttpURLConnection con =
(HttpURLConnection)url.openConnection();
con.setConnectTimeout(30000);
con.setUseCaches(false);
//바로 출력
InputStream is = url.openStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
//Message에 저장
Message message = new Message();
message.obj = bitmap;
imageHandler.sendMessage(message);
}catch(Exception e){
Log.e("이미지 다운로드 실패", e.getMessage());
}
}
}
@Override
public void onResume(){
super.onResume();
new TextThread().start();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_item_detail);
//디자인 한 뷰 찾아오기
lblitemname = (TextView)findViewById(R.id.lblitemname);
lbldescription = (TextView)findViewById(R.id.lbldescription);
lblprice = (TextView)findViewById(R.id.lblprice);
imgpictureurl = (ImageView)findViewById(R.id.imgpictureurl);
}
}
**Android에서 데이터 삽입
=> itemname, description, price를 파라미터로 전송해야 하고, pictureurl이 이미지 파일
1. 실행가능한 Activity 생성 - InsertActivity
2. 화면 디자인 - EditText 3개와 버튼 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"
android:orientation="vertical"
tools:context=".InsertActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Input ID!"
android:id="@+id/itemnameinput"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Input Price!"
android:id="@+id/priceinput"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Input Description!"
android:id="@+id/descriptioninput"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btninsert"
android:text="데이터 추가"/>
</LinearLayout>
3. res 디렉토리에 raw 디렉토리를 생성하고, ball.png 파일 복사
4. InsertActivity.java 파일 작성
public class InsertActivity extends AppCompatActivity {
EditText itemnameinput, priceinput, descriptioninput;
Button btninsert;
class InsertThread extends Thread{
public void run(){
try{
//upload 할 주소 만들기
URL url = new URL(
"http://192.168.0.200:9000/mysqlserver" +
"/insert");
//서버에게 넘겨줄 문자열 파라미터를 생성
String [] data = {itemnameinput.getText().toString().trim(),
priceinput.getText().toString().trim(),
descriptioninput.getText().toString().trim()};
String [] dataName ={"itemname", "price", "description"};
//파라미터 전송에 필요한 변수를 생성
String lineEnd = "\r\n";
//파일 업로드를 할 때는 boundary 값이 있어야 합니다.
//랜덤하게 생성하는 것을 권장
//String boundary = "androidinsert";
String boundary = UUID.randomUUID().toString();
//업로드 옵션을 설정
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setRequestMethod("POST");
con.setConnectTimeout(30000);
con.setUseCaches(false);
con.setDoInput(true);
con.setDoOutput(true);
//파일 업로드 옵션 설정
con.setRequestProperty("ENCTYPE", "multipart/form-data");
con.setRequestProperty("Content-Type",
"multipart/form-data;boundary=" + boundary);
//문자열 파라미터를 전송
String delimiter = "--" + boundary + lineEnd;
StringBuffer postDataBuilder = new StringBuffer();
for(int i=0; i<data.length; i=i+1){
postDataBuilder.append(delimiter);
postDataBuilder.append(
"Content-Disposition: form-data; name=\"" +
dataName[i] + "\"" + lineEnd + lineEnd + data[i] + lineEnd);
}
//업로드할 파일이 있는 경우에만 작성
String fileName = "ball.png";
if(fileName != null){
postDataBuilder.append(delimiter);
postDataBuilder.append(
"Content-Disposition: form-data; name=\"" +
"pictureurl" + "\";filename=\"" + fileName + "\"" +
lineEnd);
}
//파라미터 전송
DataOutputStream ds = new DataOutputStream(con.getOutputStream());
ds.write(postDataBuilder.toString().getBytes());
//파일 업로드
if(fileName != null){
ds.writeBytes(lineEnd);
//파일 읽어오기 - id에 해당하는 파일을 raw 디렉토리에 복사
InputStream fres = getResources().openRawResource(
R.raw.ball);
byte [] buffer = new byte[fres.available()];
int length = -1;
//파일의 내용을 읽어서 읽은 내용이 있으면 그 내용을 ds에 기록
while((length = fres.read(buffer))!= -1){
ds.write(buffer, 0, length);
}
ds.writeBytes(lineEnd);
ds.writeBytes(lineEnd);
ds.writeBytes("--"+boundary+"--"+lineEnd);
fres.close();
}else{
ds.writeBytes(lineEnd);
ds.writeBytes("--"+boundary+"--"+lineEnd);
}
ds.flush();
ds.close();
//서버로 부터의 응답 가져오기
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");
}
br.close();
con.disconnect();
//JSON 파싱
JSONObject object = new JSONObject(sb.toString());
boolean result = object.getBoolean("result");
//핸들러에게 결과를 전송
Message message = new Message();
message.obj = result;
insertHandler.sendMessage(message);
}catch(Exception e){
Log.e("업로드 예외", e.getMessage());
}
}
}
Handler insertHandler =
new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message message){
boolean result = (Boolean)message.obj;
if(result == true){
Toast.makeText(InsertActivity.this,
"삽입 성공", Toast.LENGTH_LONG).show();
//키보드 내리기
InputMethodManager imm =
(InputMethodManager)getSystemService(
INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(
itemnameinput.getWindowToken(), 0);
imm.hideSoftInputFromWindow(
priceinput.getWindowToken(), 0);
imm.hideSoftInputFromWindow(
descriptioninput.getWindowToken(), 0);
}else{
Toast.makeText(InsertActivity.this,
"삽입 실", Toast.LENGTH_LONG).show();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_insert);
itemnameinput = (EditText)findViewById(R.id.itemnameinput);
priceinput = (EditText)findViewById(R.id.priceinput);
descriptioninput = (EditText)findViewById(R.id.descriptioninput);
btninsert = (Button)findViewById(R.id.btninsert);
btninsert.setOnClickListener(new Button.OnClickListener(){
public void onClick(View view){
//유효성 검사
if(itemnameinput.getText().
toString().trim().length() < 1){
Toast.makeText(InsertActivity.this,
"이름은 필수 입력입니다.",
Toast.LENGTH_LONG).show();
return;
}
if(priceinput.getText().
toString().trim().length() < 1){
Toast.makeText(InsertActivity.this,
"가격은 필수 입력입니다.",
Toast.LENGTH_LONG).show();
return;
}
if(descriptioninput.getText().
toString().trim().length() < 1){
Toast.makeText(InsertActivity.this,
"설명은 필수 입력입니다.",
Toast.LENGTH_LONG).show();
return;
}
new InsertThread().start();
}
});
}
}
5. 서버와 안드로이드 앱을 모두 구동해서 테스트
1) 안드로이드 화면에 삽입 성공이 출력되는지 확인
2) 데이터베이스에서 확인
3) 서버 프로젝트를 저장한 디렉토리 확인
=> .metadata 디렉토리의 .plugins/org.eclipse.wst.server.core/tmp0()/wtpwebapps/프로젝트명/업로드디렉토리명/ 을 확인
**회원관리를 위한 서버와 안드로이드 앱 만들기
=> 회원 정보
- email(primary key - 복호화가 가능한 암호화를 이용해서 저장)
- nickname(unique - 로그인 할 때 아이디로 사용)
- pw(not null - 복호화는 불가능하고 비교만 가능한 형태의 암호화를 이용해서 저장)
- profile(이미지 파일의 경로를 저장 - 기본 이미지 이름은 default.jpg)
=> 회원가입, 로그인, 회원정보 수정, 회원 탈퇴 4가지 작업 구현
1. 데이터를 설계 - 데이터베이스 작업
CREATE TABLE Member(
email varchar(200) primary key,
nickname varchar(200) unique,
pw varchar(100) not null,
profile varchar(100)
)engine=InnoDB DEFAULT CHARSET=utf8;
2. Server 작업
1) 암호화 작업을 위한 준비
=> pom.xml 파일에 아래 dependency가 설정되었는지 확인
<!-- 복호화가 불가능한 암호화 라이브러리 -->
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
=> 복호화가 가능한 암호화를 위한 클래스를 프로젝트에 삽입
2) webapp 디렉토리에 파일을 저장할 디렉토리를 생성
=> profile 디렉토리를 생성
=> 서버에서 데이터를 제공하는 디렉토리를 만들 때는 텍스트 파일을 만들어서 마지막 업데이트 한 날짜를 저장
3) 유저의 기본 이미지로 사용할 default.jpg 파일을 profile 디렉토리에 복사
4) Member 테이블과 연동할 클래스를 domain 패키지에 생성
import lombok.Data;
@Data
public class Member {
private String email;
private String pw;
private String nickname;
private String profile;
}
=> 자동으로 getter, setter 생성되지 않으면 직접 생성
5) Member 테이블과 Member 클래스를 매핑하는 파일을 dao 패키지에 생성
=> member.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="kakao.itggangpae.mysqlserver.domain">
<class name="Member" table="Member">
<id name="email" column="email"/>
<property name="pw" column="pw"/>
<property name="nickname" column="nickname"/>
<property name="profile" column="profile"/>
</class>
</hibernate-mapping>
6) 매핑 파일을 등록
=> root-context.xml 파일에 추가
- web.xml 파일의 listener 태그 근처 context-param 태그에 설정된 파일에 작성
- 웹 애플리케이션이 시작될 때 사용할 bean의 설정 파일 경로
<!-- 하이버네이트 설정 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>
kakao/itggangpae/mysqlserver/dao/item.hbm.xml
</value>
<value>
kakao/itggangpae/mysqlserver/dao/member.hbm.xml
</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
</value>
</property>
</bean>
5) MemberDAO 클래스를 만들고 필요한 메소드를 구현
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import kakao.itggangpae.mysqlserver.domain.Member;
@Repository
public class MemberDAO {
@Autowired
private SessionFactory sessionFactory;
//email 중복검사를 위한 메소드
public List<String> emailcheck(){
List<String> list =
sessionFactory.getCurrentSession()
.createNativeQuery(
"select email from member")
.getResultList();
return list;
}
//nickname 중복검사를 위한 메소드
public List<String> nicknamecheck(String nickname){
List<String> list =
sessionFactory.getCurrentSession()
.createNativeQuery(
"select nickname from member "
+ "where nickname = \'" + nickname
+ "\'")
.getResultList();
return list;
}
//회원가입을 위한 메소드
public void join(Member member) {
sessionFactory.getCurrentSession().save(member);
}
//로그인을 위한 메소드
//nickname 과 pw를 가지고 로그인
//nickname을 가지고 모든 정보를 전부 찾아가면 됩니다.
public List<Member> login(String nickname){
List<Member> list =
sessionFactory.getCurrentSession()
.createNativeQuery(
"select nickname, pw, email, profile "
+ "from member "
+ "where nickname = \'" + nickname
+ "\'")
.getResultList();
return list;
}
//회원정보를 수정하는 메소드
public void update(Member member) {
//다른 SQL 작업과 혼합이 되는 경우 한꺼번에 수행할 때는
//update 대신에 merge를 사용하며
//없으면 저장하고 있으면 수정하고자 하는 경우에는 saveOrUpdate
sessionFactory.getCurrentSession().merge(member);
}
//회원정보를 삭제하는 메소드
public void delete(Member member) {
//이전에 수행 중인 내용을 전부 삭제하고 작업을 수행
sessionFactory.getCurrentSession().clear();
sessionFactory.getCurrentSession().delete(member);
}
}
6) MemberService 인터페이스를 만들고 필요한 메소드를 선언
=> 파라미터 모양이나 리턴 타입이 여러가지형태
- 파라미터는 클라이언트로부터 전송받은 데이터를 읽기 위한 것
- 리턴 타입은 클라이언트에게 작업의 결과를 넘겨주기 위한 것
- 파라미터는 httpServletRequest(파일 업로드 X), MultipartHttpServletRequest(파일 업로드 O)를 이용
- 리턴 타입은 전부 void 로 하고 데이터는 request의 attribute를 이용하여 저장
=> 실무에서는 필요한 것암 파라미터로 받고 필요한 데이터만 리턴하는 형태로 작업
=> 서비스의 메소드는 클라이언트의 요청 개수 만큼 생성
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.multipart.MultipartHttpServletRequest;
public interface MemberService {
//회원 가입을 메소드
public void join(MultipartHttpServletRequest request);
//로그인을 위한 메소드
public void login(HttpServletRequest request);
//회원정보 수정을 위한 메소드
public void update(MultipartHttpServletRequest request);
//회원탈퇴를 위한 메소드
public void delete(HttpServletRequest request);
}
3. Client Application 작업 - Android
**회원가입 작업
1) 샘플 이미지를 삽입
=> res/raw 디렉토리에 삽입
2) 실행가능한 Activity 추가 - MemberJoinActivity
<?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=".MemberJoinActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email을 입력하시오"
android:inputType="textEmailAddress"
android:id="@+id/emailinput"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password을 입력하시오"
android:inputType="textPassword"
android:id="@+id/pwinput"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="별명을 입력하시오"
android:inputType="text"
android:id="@+id/nicknameinput"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/btnjoin"
android:text="회원가입"
android:layout_weight="1"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/btnlogin"
android:text="로그인"
android:layout_weight="1"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/btnmain"
android:text="메인"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>
입력을 받아서
TIP
**MyBatis
=> xml 을 이용한 SQL
=> 인터페이스를 이용한 SQL
**Hibernate
=> Hibernate가 제공하는 메소드를 이용한 작업
=> HQL이라는 하이버네이트에서 제공하는 쿼리를 이용하는 작업
=> Native Query라는 전통적 SQL을 이용하여 작업
- Native Query를 추천하지 않았던 이유 : SQL은 절차적 언어가 아니고 데이터베이스 종류마다 다르기 때문
- 개발자들은 대부분 SQL에 익숙하기 때문에 SQL을 사용하는 것에 익숙
**JSON Parsing
=> JSON : 자바 스크립트 데이터 표현 방식으로 데이터를 표현
=> [ ] : Array, { } : Map
- Array(List) : 인덱스를 이용하여 데이터를 조회 - 길이를 알아야 함
- Map
**HttpURLConnection을 이용한 post 전송
=> 전송하는 방법이 어려워서 별도의 라이브러리를 다운로드 받아서 사용하기도 함
=> 다른 라이브러리를 사용하는 경우, 라이브러리에 종속되고, 스마트 폰 프로그래밍에서는 조심해서 사용해야 함
=> 스마트폰 프로그래밍은 마켓에서 심사를 진행(라이브러리에 위험한 코드 내포시 reject)
=> 파일 전송시 Enctype을 multipart/form-data로 설정해야 함
- Content-Type도 multipart/form-data 그리고 boundary까지 설정해야 함
=> 직접 입력을 받아 서버에 전송시 데이터의 유효성을 검사하여 통과한 경우에만 전송
'수업 정리' 카테고리의 다른 글
81일차 수업정리(Android 연동) (0) | 2020.07.30 |
---|---|
80일차 수업정리(XML Parsing, Adapter) (0) | 2020.07.29 |
75~76일차 수업 정리(Oracle&MyBatis) (0) | 2020.07.23 |
75일차 수업정리(Android - Socket) (0) | 2020.07.22 |
74일차 수업 정리(Android - Thread) (0) | 2020.07.21 |