Oracle
계정 생성
SQL
1
2
3
CREATE USER android IDENTIFIED BY android DEFAULT TABLESPACE USERS;
GRANT connect, resource TO android;
ALTER USER android ACCOUNT UNLOCK;테이블 생성
SQL
1
2
3
4
5
CREATE TABLE users (
id VARCHAR2(255),
password VARCHAR2(255),
enabled NUMBER(1) DEFAULT 1
);데이터 삽입
SQL
1
INSERT INTO users (id, password) VALUES ('dico', 'dico');Spring
프로젝트 생성
Spring Legacy Project > Spring MVC Project (Package Name: spring.mvc.android)
pom.xml
MyBatis와 JSON을 사용하기 위한 jar 추가
Plain Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>https://mvnrepository.com에서 직접 다운로드 가능
context.xml
Servers>Tomcat [version] Server at localhost-config에 위치.
데이터베이스 연결 정보 추가.
1
2
3
4
5
6
7
8
9
10
<Resource auth="Container"
driverClassName="oracle.jdbc.driver.OracleDriver"
maxActive="50"
maxWait="1000"
name="jdbc/Oracle11g/android"
username="android"
password="android"
type="javax.sql.DataSource"
url="jdbc:oracle:thin:@localhost:1521:xe"
/>servlet-context.xml
Connection Pool에 접근하는 bean 생성.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- MyBatis -->
<!-- DAO에서 사용할 Connection Pool 접근 변수 -->
<beans:bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<beans:constructor-arg index="0" ref="sqlSessionFactory" />
</beans:bean>
<beans:bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- Connection Pool 호출 -->
<beans:property name="dataSource" ref="dataSource" />
<!-- mapper.xml 경로 설정 -->
<beans:property name="mapperLocations" value="classpath:android/*.xml" />
</beans:bean>
<!-- 3. Connection Pool 접속 -->
<beans:bean name="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<!-- context.xml의 ConnectionPool lookup -->
<beans:property name="jndiName" value="java:comp/env/jdbc/Oracle11g/android" />
<beans:property name="resourceRef" value="true" />
</beans:bean>Users Class
VO Class 생성.
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Users {
private String id;
private String password;
private int enabled;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getEnabled() {
return enabled;
}
public void setEnabled(int enabled) {
this.enabled = enabled;
}
}DAO
Java
1
2
3
public interface AndroidDAO {
public String confirmUser(Users user);
}Java
1
2
3
4
5
6
7
8
9
10
11
12
13
@Repository
public class AndroidDAOImpl implements AndroidDAO {
@Autowired
SqlSession sqlSession;
@Override
public String confirmUser(Users user) {
AndroidDAO mapper = sqlSession.getMapper(AndroidDAO.class);
String enabled = mapper.confirmUser(user);
return enabled;
}
}Mapper
src>main>resources>android>mapper.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="spring.mvc.android.persistence.AndroidDAO">
<select id="confirmUser" resultType="String">
SELECT enabled
FROM users
WHERE id = #{id} AND password = #{password}
</select>
</mapper>Controller
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Controller
public class AndroidController {
private Logger log = Logger.getLogger(this.getClass());
@Autowired
AndroidDAO dao;
@ResponseBody
@RequestMapping("androidSignIn")
public Map<String, Object> android(HttpServletRequest req) {
log.info("androidSignIn()");
String id = req.getParameter("id");
String pwd = req.getParameter("pwd");
Users user = new Users();
user.setId(id);
user.setPassword(pwd);
String enabled = dao.confirmUser(user);
if(enabled == null) {
enabled = "0";
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", id);
map.put("password", pwd);
map.put("enabled", enabled);
return map;
}
}Android
AndroidManifest.xml
인터넷 권한 부여.
1
<uses-permission android:name="android.permission.INTERNET" />첫 시작 Activity를 바꿀 경우 수정.
1
2
3
4
5
6
<application
...>
<activity android:name=".SignInActivity">
...
</activity>
</application>build.gradle(Module: app)
GSON(JSON을 편하게 사용할 수 있도록 google에서 만든 라이브러리) 추가
1
implementation 'com.google.code.gson:gson:2.8.2'Users.java
Spring의 Users Class와 동일
activity_signin.xml
로그인 화면 구성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/edt_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:layout_marginStart="32dp"
android:layout_marginTop="96dp"
android:ems="10"
android:hint="ID"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/edt_pwd"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:layout_marginStart="32dp"
android:ems="10"
android:hint="PASSWORD"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edt_id" />
<Button
android:id="@+id/btn_signin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:background="@android:color/holo_blue_dark"
android:text="SignIn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edt_pwd" />
</android.support.constraint.ConstraintLayout>Web.java
Task를 각 Activity마다 Inner Class로 사용하므로 공통 부분을 따로 뺐다.
Java
1
2
3
4
public class Web {
public static String ip = "[본인IP]";
public static String servletURL = "http://" + ip + ":[포트번호]/android/"; //URL 공통 부분
}CMD 명령어 ipconfig
HttpClient.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class HttpClient {
private int httpStatusCode; //HTTP 응답 상태 코드. (ex: 200, 401, 식별 불가능하면 -1)
private String body;
public int getHttpStatusCode() {
return httpStatusCode;
}
public String getBody() {
return body;
}
private Builder builder;
private void setBuilder(Builder builder) {
this.builder = builder;
}
//HTTP 요청 결과 수집 후 연결 해제
public void request() {
HttpURLConnection conn = getConnection();
setHeader(conn);
setBody(conn);
httpStatusCode = getStatusCode(conn);
body = readStream(conn); //요청 결과
conn.disconnect(); //연결 해제
}
private HttpURLConnection getConnection() {
try {
URL url = new URL(builder.getUrl());
return (HttpURLConnection) url.openConnection();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private void setHeader(HttpURLConnection conn) {
setContentType(conn);
setRequestMethod(conn);
conn.setConnectTimeout(30 * 1000); //서버 연결 시간 제한. 지정된 시간 안에 연결되지 않으면 SocketTimeoutException 발생
conn.setReadTimeout(15 * 1000); //Input Stream으로 읽을 때의 시간 제한. 읽기 전에 시간 초과되면 SocketTimeoutException 발생
conn.setDoOutput(true); //출력 사용 여부
conn.setDoInput(true); //입력 사용 여부
}
private void setContentType(HttpURLConnection conn) {
conn.setRequestProperty("content-Type", "application/x-www-form-urlencoded");
}
private void setRequestMethod(HttpURLConnection conn) {
try {
conn.setRequestMethod(builder.getMethod());
} catch (ProtocolException e) {
e.printStackTrace();
}
}
private void setBody(HttpURLConnection conn) {
String parameter = builder.getParameter();
if (parameter != null && parameter.length() > 0) {
OutputStream outputStream = null;
try {
outputStream = conn.getOutputStream();
outputStream.write(parameter.getBytes("UTF-8"));
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private int getStatusCode(HttpURLConnection conn) {
try {
return conn.getResponseCode();
} catch (IOException e) {
e.printStackTrace();
}
return -1;
}
private String readStream(HttpURLConnection conn) {
String result = "";
BufferedReader reader = null;
//요청 결과 읽기
try {
reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
result += line;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
//전체 URL 생성하는 Builder Pattern
public static class Builder {
private Map<String, String> parameters;
private String method;
private String url;
public String getMethod() {
return method;
}
public String getUrl() {
return url;
}
public Builder(String method, String url) {
if (method == null) {
method = "GET";
}
this.method = method;
this.url = url;
this.parameters = new HashMap<>();
}
public void addOrReplace(String key, String value) {
parameters.put(key, value);
}
public void addAllParameters(Map<String, String> param) {
parameters.putAll(param);
}
public String getParameter() {
return generateParameters();
}
public String getParameter(String key) {
return this.parameters.get(key);
}
private String generateParameters() {
StringBuffer urlParameters = new StringBuffer();
Iterator keys = getKeys();
//전달할 값을 URL 형식으로 바꾸기
String key = "";
while (keys.hasNext()) {
key = (String) keys.next();
urlParameters.append(String.format("%s=%s", key, parameters.get(key)));
urlParameters.append("&");
}
String params = urlParameters.toString();
if (params.length() > 0) {
params = params.substring(0, params.length() - 1);
}
return params;
}
private Iterator getKeys() {
return this.parameters.keySet().iterator();
}
public HttpClient create() {
HttpClient client = new HttpClient();
client.setBuilder(this);
return client;
}
}
}SignInActivity.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.google.gson.Gson;
import java.util.HashMap;
import java.util.Map;
public class SignInActivity extends AppCompatActivity {
private EditText edtId, edtPwd;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_signin);
edtId = findViewById(R.id.edt_id);
edtPwd = findViewById(R.id.edt_pwd);
Button btnSignIn = findViewById(R.id.btn_signin);
btnSignIn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Map<String, String> map = new HashMap<>();
map.put("id", edtId.getText().toString());
map.put("pwd", edtPwd.getText().toString());
MapTask task = new MapTask();
task.execute(map);
}
});
}
public class MapTask extends AsyncTask<Map, Integer, String> {
//doInBackground 전에 동작
@Override
protected void onPreExecute() {
super.onPreExecute();
}
//작업을 쓰레드로 처리
@Override
protected String doInBackground(Map... maps) {
//HTTP 요청 준비
HttpClient.Builder http = new HttpClient.Builder("POST", Web.servletURL + "androidSignIn");
//Parameter 전송
http.addAllParameters(maps[0]);
//HTTP 요청 전송
HttpClient post = http.create();
post.request();
//응답 상태 코드
int statusCode = post.getHttpStatusCode();
//응답 본문
String body = post.getBody(); //Spring의 Controller에서 반환한 값. JSON 형식
return body;
}
/*
doInBackground 후에 동작.
String s : doInBackground에서 반환한 body
*/
@Override
protected void onPostExecute(String s) {
//super.onPostExecute(s);
Log.d("JSON_RESULT", s);
//JSON 형식의 데이터를 Class Object로 바꿔준다.
Gson gson = new Gson();
Users user = gson.fromJson(s, Users.class);
if(user != null && user.getEnabled() != 0) {
Toast.makeText(getApplicationContext(), "로그인", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "회원 정보가 올바르지 않습니다.", Toast.LENGTH_SHORT).show();
}
}
}
}Plain Text
1
D/JSON_RESULT: {"password":"dico","id":"dico","enabled":"1"}
[도서] 내 문장이 그렇게 이상한가요?
Review

[Android] RecyclerView
RecyclerView
Android
