본문 바로가기

수업 정리

76~78일차 수업정리(MySQL&Hibernate Project)

 

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&amp;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까지 설정해야 함

    => 직접 입력을 받아 서버에 전송시 데이터의 유효성을 검사하여 통과한 경우에만 전송