1. Sqlite DB 테이블 생성하고 CSS 파일 만들기
CSS는 src > main > webapp > resources 아래에 넣는다.
2. 기본 폼
HomeController.java
package com.KOPO.Users;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
return "home";
}
@RequestMapping(value = "/signup", method = RequestMethod.GET)
public String signup(Locale locale, Model model) {
return "signup";
}
@RequestMapping(value = "/signin", method = RequestMethod.GET)
public String signin(Locale locale, Model model) {
return "signin";
}
}
DataReader.java
package com.KOPO.Users;
import java.sql.Connection;
import java.sql.DriverManager;
import org.sqlite.SQLiteConfig;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.sql.ResultSet;
public class DataReader {
private Connection connection;
private String dbfileName;
private String dbTableName;
static {
try {
Class.forName("org.sqlite.JDBC");
}catch (Exception e) {
e.printStackTrace();
}
}
public DataReader(String databaseFileName, String dbTableName) {
this.dbfileName = databaseFileName;
this.dbTableName = dbTableName;
}
public boolean open() {
try {
SQLiteConfig config = new SQLiteConfig();
this.connection = DriverManager.getConnection("jdbc:sqlite:/" +
this.dbfileName, config.toProperties());
}catch(SQLException e) {
e.printStackTrace();
return false;
}
return true;
}
public boolean close() {
if(this.connection == null) {
return true;
}
try {
this.connection.close();
}catch(SQLException e) {
e.printStackTrace();
return false;
}
return true;
}
}
home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>
User Data
</h1>
<a href="/Users/private">개인정보</a><br />
<a href="/Users/signin">로그인</a> <a href="/Users/signout">로그아웃</a> <a href="/Users/signup">회원가입</a><br/><br/>
</body>
</html>
위 기본 폼에 명령을 추가하고(HomeController), SQL을 만들고(DataReader), 페이지를 만든다(.jsp)
3. 기능 만들기 (C : Insert)
계정을 만들어보자.
1. View를 수정하자.(signup.jsp)
HTML 코드 부분은 atom에서 작성해서 STS에 복붙한다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/sign_up.css">
<title>회원가입</title>
</head>
<body>
<h1>회원가입</h1>
<a href="/Users/">홈으로</a><br /><br />
<!-- form 태그는 type="submit" 버튼을 누르면 action을 실행한다. name은 각각 파라미터 이름이 되고, 입력된 값은 각 파라미터의 값이 된다. HomeController에서 HttpServletRequest가 .getParameter를 통해 파라미터와 값에 접근한다. -->
<form action="/Users/signup_action" method = "post">
이름 <input type="text" name="name" placeholder="이름을 입력해주세요" />
<br />
ID <input type="text" name="id" placeholder="아이디를 입력해주세요" />
<br />
PW <input type="password" name="password" placeholder="패스워드를 입력해주세요" />
<br /><br />
<input type="submit" name="" value="회원가입" />
</form>
</body>
</html>
2. DB와 통신하는 Model를 수정하자.(DataReader.java)
// signupAction에 대한 Insert 쿼리를 날려주는 메소드를 생성한다.
public int insertData(String name, String id, String password) throws SQLException {
String query = "INSERT INTO " + this.dbTableName + " (name, id, password) VALUES('" + name + "', '" + id + "', '" + password + "');";
Statement statement = this.connection.createStatement(); // Statement를 이용해 접속 엔진을 연다.
int result = statement.executeUpdate(); // 엔진을 이용해 쿼리를 보낸다.
statement.close(); // 접속 엔진을 닫는다.
return result;
}
String query = "INSERT INTO " + this.dbTableName + " (name, id, password) VALUES('" + name + "', '" + id + "', '" + password + "');";
String query = "INSERT INTO " + this.dbTableName + " (name, id, password) VALUES('" + name + "', '" + id + "', '" + password + "');''";
위에꺼가 맞는거다. 변수의 값을 문자로 보내주기 위해서 '변수명'으로 넣어주려는 것으로 ); 앞뒤로 ' ' 을 쓰는게 아니다.
3. Request를 받아 DataReader를 시켜 DB에 쿼리를 보내고, View를 시켜 Response를 보내는 @RequestMapping을 만들자.(HomeController.java)
@RequestMapping(value = "/signup", method = RequestMethod.GET)
public String signup(Locale locale, Model model) {
return "signup";
}
// signup 페이지에 '회원가입'버튼을 만들고, 그 버튼을 누를 경우 무언가 기능을 실행시키도록 명령할 request를 만든다.
// 데이터를 감추기 위해 request 방식은 POST 방식으로 바꾼다.
@RequestMapping(value = "/signup_action", method = RequestMethod.POST) // 어떤 Request를 받으면 동작할건가?
public String signupAction(Locale locale, Model model, HttpServletRequest request) throws UnsupportedEncodingException {
request.setCharacterEncoding("UTF-8"); // UTF-8 인코딩.
String name = request.getParameter("name"); // name이라는 파라미터의 값을 얻는다.
String id = request.getParameter("id");
String password = request.getParameter("password");
// DataReader에 Insert 쿼리를 보내는 메소드를 만들고 그것을 실행하도록 명령을 내린다.
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
DataReader dataReader = new DataReader(dbUrl, "users");
dataReader.open(); // DataReader를 연다.
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256"); // SHA 알고리즘 클래스 호출.
byte[] hash = digest.digest(password.getBytes("UTF-8")); // 암호화 할 데이터를 "UTF-8"로 읽어 위에서 만든 알고리즘을 이용해 해쉬를 한다.
StringBuffer hexString = new StringBuffer(); // 문자열을 잠시 담기 위한 버퍼 호출.
// 해쉬한 문자열을 16진수(hex)로 바꿔주는 로직.
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
// 이 위에는 password 변수가 담은 값을 SHA-256을 하는 과정이고, 실제 DB에 Insert 쿼리를 보내는건 아래 한 줄이다.
dataReader.insertData(name, id, hexString.toString()); // password 변수 대신 hexString.toString()을 넣어야 SHA-256 암호화 한 데이터를 보낸다.
} catch (Exception ex) {
throw new RuntimeException(ex); // RuntimeException을 추가한다.
}
dataReader.close(); // DataReader를 닫는다.
return "signup"; // 어떤 View를 통해 Response를 보낼 것인가?
}
'1234'로 입력한 비밀번호가 SHA-256으로 암호화 되어 들어갔다.
Statement를 PreparedStatement로 바꿔보자
// signupAction을 Statement에서 PreparedStatement로 바꿔보자.
public int insertData(String name, String id, String password) throws SQLException {
String query = "INSERT INTO " + this.dbTableName + " (name, id, password) VALUES(?, ?, ?);";
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // PreparedStatement를 이용해 접속 엔진을 연다.
preparedStatement.setString(1, name); // 1번째 물음표에 name을 넣는다.
preparedStatement.setString(2, id); // 2번째 물음표에 id를 넣는다.
preparedStatement.setString(3, password);
int result = preparedStatement.executeUpdate(); // 엔진을 이용해 쿼리를 보낸다.
preparedStatement.close(); // 접속 엔진을 닫는다.
return result;
}
Statement는 쿼리문을 그대로 한 줄로 넣고,
PreparedStatement는 쿼리문에서 파라미터를 따로 분리한다.
4. 기능 만들기 (R : Select)
아이디를 생성했으니 로그인을 해보자.
1. View를 수정하자.(signin.jsp)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/sign_up.css">
<title>로그인</title>
</head>
<body>
<h3>로그인</h3>
<a href="/Users/">홈으로</a><br /><br />
<form action="/Users/signin_action" method="post">
ID <input type="text" name="id" placeholder="아이디를 입력해주세요" />
<br />
PW <input type="text" name="password" placeholder="패스워드를 입력해주세요" />
<br />
<input type="submit" value="로그인" />
</form>
</body>
</html>
2. DB와 통신하는 Model를 수정하자.(DataReader.java)
// signinAction에 대한 Select 쿼리를 날려주는 메소드를 생성한다.
public boolean signin(String id, String password) throws SQLException {
boolean result = false;
String query = "SELECT * FROM " + this.dbTableName + " WHERE id=? AND password=?;";
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
preparedStatement.setString(1, id);
preparedStatement.setString(2, password);
ResultSet resultSet = preparedStatement.executeQuery(); // ResultSet은 쿼리 결과를 그대로 받아온다.
if (resultSet.next()) {
result = true; // ResultSet을 이용해 일치하면 return값인 result에 boolean 형태의 값을 넣너준다.
}
resultSet.close(); // 반드시 닫아준다!
preparedStatement.close(); // 반드시 닫아준다!
return result;
}
3. Request를 받아 DataReader를 시켜 DB에 쿼리를 보내고, View를 시켜 Response를 보내는 @RequestMapping을 만들자.(HomeController.java)
@RequestMapping(value = "/signin", method = RequestMethod.GET)
public String signin(Locale locale, Model model) {
return "signin";
}
// @RequestParam으로 받을 경우 한글이 깨진다. (영어는 괜찮음. 이 경우는 id, password 모두 영어만 있어서 괜찮지만 위 signupAction처럼 HttpServletRequest request로 통일시키자.
@RequestMapping(value = "/signin_action", method = RequestMethod.POST)
public String signinAction(Locale locale, Model model, HttpServletRequest request,
@RequestParam("id") String id,
@RequestParam("password") String password) {
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
DataReader dataReader = new DataReader(dbUrl, "users");
boolean result = false;
dataReader.open(); // DataReader를 연다.
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(password.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();
// 해쉬한 문자열을 16진수(hex)로 바꿔주는 로직.
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
result = dataReader.signin(id, hexString.toString()); // DB에 SHA-256으로 해시한 암호가 저장되어 있으니 사용자가 입력한 암호 역시 SHA-256으로 해시해서 보내야 일치하는지 판별이 가능하다.
} catch (Exception ex) {
throw new RuntimeException(ex); // RuntimeException을 추가한다.
}
dataReader.close(); // DataReader를 닫는다.
if (result) {
HttpSession session = request.getSession();
session.setAttribute("is_login", "true");
return "success";
} else {
return "fail";
}
}
// 가급적 signupAction처럼 HttpServletRequest를 이용해 .getParameter(); 를 이용하는 방식으로 통일시키자.(한글 깨짐 방지)
@RequestMapping(value = "/signin_action", method = RequestMethod.POST)
public String signinAction(Locale locale, Model model, HttpServletRequest request) throws UnsupportedEncodingException {
request.setCharacterEncoding("UTF-8"); // UTF-8 인코딩.
String id = request.getParameter("id");
String password = request.getParameter("password");
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
DataReader dataReader = new DataReader(dbUrl, "users");
boolean result = false;
dataReader.open(); // DataReader를 연다.
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(password.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();
// 해쉬한 문자열을 16진수(hex)로 바꿔주는 로직.
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
result = dataReader.signin(id, hexString.toString()); // DB에 SHA-256으로 해시한 암호가 저장되어 있으니 사용자가 입력한 암호 역시 SHA-256으로 해시해서 보내야 일치하는지 판별이 가능하다.
} catch (Exception ex) {
throw new RuntimeException(ex); // RuntimeException을 추가한다.
}
dataReader.close(); // DataReader를 닫는다.
if (result) {
HttpSession session = request.getSession();
session.setAttribute("is_login", "true");
return "success";
} else {
return "fail";
}
}
결과를 보여줄 View(success, fail)을 만들자
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>signin success</title>
</head>
<body>
<p>로그인 되었습니다.</p>
<a href="/Users/">홈으로</a>
</body>
</html>
fail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>signin fail</title>
</head>
<body>
<p>로그인에 실패하였습니다. 아이디와 비밀번호를 확인하세요.</p>
<a href="/Users/">홈으로</a> <a href="/Users/signin">로그인 페이지로 돌아가기</a>
</body>
</html>
5. 기능 만들기 (로그인 세션 확인하고 로그아웃 만들기)
private.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>비밀 페이지 입니다.</h3>
<a href="/Users/">홈으로</a>
</body>
</html>
HomeController
// 로그인 세션 확인 페이지로 이동.
@RequestMapping(value = "/private", method = RequestMethod.GET)
public String privatePage(Locale locale, Model model, HttpServletRequest request) {
HttpSession session = request.getSession();
String isLogin = (String)session.getAttribute("is_login");
if (isLogin != null && isLogin.equals("true")) {
return "private";
}
return "signin";
}
// 세션 로그아웃.
@RequestMapping(value = "/signout", method = RequestMethod.GET)
public String logout(Locale locale, Model model, HttpServletRequest request) {
HttpSession session = request.getSession();
session.invalidate(); // 세션을 무효화한다.
return "signin";
}
6. 기능 만들기 (DB 데이터 테이블로 뿌리기)
home.jsp
테이블을 추가한다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/home.css">
</head>
<body>
<h1>
User Data
</h1>
<a href="/Users/private">개인정보</a><br />
<a href="/Users/signin">로그인</a> <a href="/Users/signout">로그아웃</a> <a href="/Users/signup">회원가입</a><br/><br/>
<table>
<thead>
<tr>
<th>idx</th><th>이름</th><th>아이디</th><th></th>
</tr>
</thead>
<tbody>
${userInfo }
</tbody>
</table>
</body>
</html>
DataReader.java
public String selectData() throws SQLException {
boolean result = false;
String query = "SELECT * FROM " + this.dbTableName + " WHERE ?;";
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
preparedStatement.setInt(1, 1);
ResultSet resultSet = preparedStatement.executeQuery();
String htmlText = "";
while (resultSet.next()) {
String idx = resultSet.getString("idx");
String name = resultSet.getString("name");
String id = resultSet.getString("id");
htmlText = htmlText
+ String.format("<tr>"
+ "<td>%s</td>"
+ "<td>%s</td>"
+ "<td>%s</td>"
+ "<td><a href='/Users/modify?idx=%s'>수정하기</a></td>",
idx, name, id, idx);
}
resultSet.close();
preparedStatement.close();
return htmlText;
}
htmlText = htmlText + String.format("<tr><td>%s</td><td>%s</td><td>%s</td><td><a href='/Users/modify?idx=%s'>수정하기</a></td>", idx, name, id, idx);
이렇게 한 줄로 쓰고 엔터 치면 + 버튼은 알아서 들어간다.
HomeController.java
DataReader를 시켜서 SELECT 쿼리를 보내고, Home(View)를 이용해 Response를 한다.
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
// DB 테이블 존재 유무를 콘솔에 출력.
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
File f = new File(dbUrl);
if (f.exists()) {
System.out.println("DB 테이블 있음.");
} else {
System.out.println("DB 테이블 없음.");
}
// DB 에서 읽어온 데이터를 테이블로 뿌리기
DataReader dataReader = new DataReader(dbUrl, "users");
dataReader.open();
try {
String result = dataReader.selectData();
model.addAttribute("userInfo", result);
} catch (Exception e) {
}
dataReader.close();
return "home";
}
'수정하기'버튼을 누르면 URL이 이렇게 뜨면 된다.(해당 URL 접속 후의 기능은 아직 구현하지 않았고 URL이 뜨는 것만 확인하면 됩니다.)
(idx=13인 홍길동의 '수정하기'를 누른 결과)
7. 기능 만들기 (U : Update)
modify.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/sign_up.css">
<title>정보수정</title>
</head>
<body>
<h1>정보수정</h1>
<a href="/Users/">홈으로</a><br /><br />
<!-- form 태그는 type="submit" 버튼을 누르면 action을 실행한다. name은 각각 파라미터 이름이 되고, 입력된 값은 각 파라미터의 값이 된다. HomeController에서 HttpServletRequest가 .getParameter를 통해 파라미터와 값에 접근한다. -->
<form action="/Users/modify_action" method = "post">
<input type="hidden" name="dix" value="${idx }" /> <!-- value에 DB에 있는 현재 정보를 불러와 띄워준다. -->
이름 <input type="text" name="name" placeholder="이름을 입력해주세요" value="${name }" />
<br />
ID <input type="text" name="id" placeholder="아이디를 입력해주세요" value="${id }" />
<!-- <br />
<input type="password" name="password" placeholder="패스워드를 입력해주세요" /> -->
<br /><br />
<input type="submit" name="" value="수정하기" />
</form>
</body>
</html>
User.java 클래스를 만들어 객체로 관리해보자
package com.KOPO.Users;
public class User {
int idx;
String name;
String id;
String password;
}
DataReader.java (객체를 이용하는 방식 - modify)
public User selectData(String idx) throws SQLException {
boolean result = false;
String query = "SELECT * FROM " + this.dbTableName + " WHERE idx=?;";
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
preparedStatement.setString(1, idx);
ResultSet resultSet = preparedStatement.executeQuery();
User selectedUser = new User();
if (resultSet.next()) {
selectedUser.idx = resultSet.getInt("idx");
selectedUser.name = resultSet.getString("name");
selectedUser.id = resultSet.getString("id");
}
resultSet.close();
preparedStatement.close();
return selectedUser;
}
HomeController.java (객체를 이용하는 방식 - modify)
@RequestMapping(value = "/modify", method = RequestMethod.GET)
public String modify(Locale locale, Model model, @RequestParam("idx") String idx) {
model.addAttribute("idx", idx);
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
DataReader dataReader = new DataReader(dbUrl, "users");
dataReader.open();
try {
User sUser = dataReader.selectData(idx); // 자료형:User 클래스, 변수명:sUser, 값:DataReader가 SELECT한 idx 번호의 객체 row.
model.addAttribute("name", sUser.name);
model.addAttribute("id", sUser.id);
} catch (Exception e) {
// TODO: handle exception
}
dataReader.close();
return "modify";
}
DataReader.java (객체를 이용하는 방식 - modify_action)
public void updateData(String idx, String name, String id) throws SQLException {
String query = "UPDATE " + this.dbTableName + " SET name=?, id=? WHERE idx=?;";
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
preparedStatement.setString(1, name);
preparedStatement.setString(2, id);
preparedStatement.setString(3, idx);
int result = preparedStatement.executeUpdate();
preparedStatement.close();
}
public void updateData(String idx, String name, String id) throws SQLException {
String query = "UPDATE " + this.dbTableName + " SET name='" + name + "', id='" + id + "' WHERE idx=" + idx + ";";
Statement statement = this.connection.createStatement();
int result = statement.executeUpdate(query);
statement.close();
}
HomeController.java (객체를 이용하는 방식 - modify_action)
@RequestMapping(value = "/modify_action", method = RequestMethod.POST)
public String modifyAction(Locale locale, Model model, HttpServletRequest request) throws UnsupportedEncodingException {
request.setCharacterEncoding("UTF-8"); // UTF-8 인코딩.
String idx = request.getParameter("idx");
String id = request.getParameter("id");
String name = request.getParameter("name");
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
DataReader dataReader = new DataReader(dbUrl, "users");
dataReader.open(); // DataReader를 연다.
try {
dataReader.updateData(idx, name, id);
} catch (Exception ex) {
throw new RuntimeException(ex); // RuntimeException을 추가한다.
}
dataReader.close(); // DataReader를 닫는다.
return "modify";
}
cf. 쿼리 보내기
String query =
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
int result = preparedStatement.executeUpdate(); 또는 ResultSet resultSet = preparedStatement.executeQuery();
String query =
Statement statement = this.connection.createStatement();
int result = statement.executeUpdate(query); 또는 ResultSet resultSet = statement.executeQuery(query);
1) PreparedStatement와 Statement는 query를 쓰는 위치가 다르다.
2) int result 는 쿼리의 실행 결과 return값이 없어도 되지만, ResultSet resultSet 는 쿼리의 실행 결과 return값이 반드시 필요하다. ResultSet은 쿼리의 실행 결과를 ResultSet이라는 객체로 받는다. 따라서, INSERT, UPDATE와 같은 return값이 없는 쿼리의 경우는 int result 를 써야하고, SELECT와 같이 return값이 있는 쿼리의 경우는 ResultSet resultSet 를 써서 return값을 객체로 받아야 다시 HomeController에 return을 할 수 있다.
cf. DataReader 메소드의 return 형태에 따른 차이
public int 메소드(인풋 파라미터) throws SQLException {
String query =
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // PreparedStatement를 이용해 접속 엔진을 연다.
preparedStatement.setString(1, 값);
preparedStatement.setString(2, 값);
int result = preparedStatement.executeUpdate(); // .executeQuery()의 return값은 boolean이라 int result에 담을 수 없다. 최종 return값인 result가 int형태기 때문에 .executeUpdate()로 쿼리를 보내야한다.
preparedStatement.close();
return result; // return값은 int 형태여야한다. (하지만 위 HomeController는 이 메소드를 호출한 다음 return값은 받지 않는다. 메소드는 return을 보내지만 HomeController에서 return값을 받지 않음.)
}
public boolean 메소드(인풋 파라미터) throws SQLException {
boolean result = false;
String query =
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
preparedStatement.setString(1, 값); // 1번째 물음표
preparedStatement.setString(2, 값); // 2번째 물음표
ResultSet resultSet = preparedStatement.executeQuery(); // ResultSet은 쿼리 결과를 그대로 받아온다.
if (resultSet.next()) {
result = true; // ResultSet을 이용해 일치하면 return값인 result에 boolean 형태의 값을 넣너준다.
}
resultSet.close();
preparedStatement.close();
return result; // return값은 boolean 형태여야한다. (위 HomeController에서 return값을 받아 사용한다.)
}
cf. 세션 처리
@RequestMapping의 메소드가 인풋 파라미터로 HttpServletRequest request를 미리 인스턴스화 해놔야한다.
// 세션에 저장하기
HttpSession session = request.getSession();
session.setAttribute(String "변수명", Object 값);
// 세션 불러오기
HttpSession session = request.getSession();
String 변수명 = (String)session.getAttribute("변수명");
// 세션 종료
HttpSession session = request.getSession();
session.invalidate();
cf. HTML 테이블 형태로 데이터를 내보내기 (DataReader -> HomeController -> VIew)
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
DataReader dataReader = new DataReader(dbUrl, "tableName");
dataReader.open();
try {
String result = dataReader.selectData();
model.addAttribute("userInfo", result);
} catch (Exception e) {
e.printStackTrace();
}
dataReader.close();
return "home";
완성본 모음
HomeController.java
package com.KOPO.Users;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
// 1. 메인 홈
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
// DB 테이블 존재 유무를 콘솔에 출력.
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
File f = new File(dbUrl);
if (f.exists()) {
System.out.println("DB 테이블 있음.");
} else {
System.out.println("DB 테이블 없음.");
}
// DB 에서 읽어온 데이터를 테이블로 뿌리기
DataReader dataReader = new DataReader(dbUrl, "users");
dataReader.open();
try {
String result = dataReader.selectData();
model.addAttribute("userInfo", result);
} catch (Exception e) {
}
dataReader.close();
return "home";
}
// 2. 회원가입
@RequestMapping(value = "/signup", method = RequestMethod.GET)
public String signup(Locale locale, Model model) {
return "signup";
}
// signup 페이지에 '회원가입'버튼을 만들고, 그 버튼을 누를 경우 무언가 기능을 실행시키도록 명령할 request를 만든다.
// 데이터를 감추기 위해 request 방식은 POST 방식으로 바꾼다.
@RequestMapping(value = "/signup_action", method = RequestMethod.POST) // 어떤 Request를 받으면 동작할건가?
public String signupAction(Locale locale, Model model, HttpServletRequest request) throws UnsupportedEncodingException {
request.setCharacterEncoding("UTF-8"); // UTF-8 인코딩.
String name = request.getParameter("name"); // name이라는 파라미터의 값을 얻는다.
String id = request.getParameter("id");
String password = request.getParameter("password");
// DataReader에 Insert 쿼리를 보내는 메소드를 만들고 그것을 실행하도록 명령을 내린다.
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
DataReader dataReader = new DataReader(dbUrl, "users");
dataReader.open(); // DataReader를 연다.
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256"); // SHA 알고리즘 클래스 호출.
byte[] hash = digest.digest(password.getBytes("UTF-8")); // 암호화 할 데이터를 "UTF-8"로 읽어 위에서 만든 알고리즘을 이용해 해쉬를 한다.
StringBuffer hexString = new StringBuffer(); // 문자열을 잠시 담기 위한 버퍼 호출.
// 해쉬한 문자열을 16진수(hex)로 바꿔주는 로직.
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
// 이 위에는 password 변수가 담은 값을 SHA-256을 하는 과정이고, 실제 DB에 Insert 쿼리를 보내는건 아래 한 줄이다.
dataReader.insertData(name, id, hexString.toString()); // password 변수 대신 hexString.toString()을 넣어야 SHA-256 암호화 한 데이터를 보낸다.
} catch (Exception ex) {
throw new RuntimeException(ex); // RuntimeException을 추가한다.
}
dataReader.close(); // DataReader를 닫는다.
return "signup"; // 어떤 View를 통해 Response를 보낼 것인가?
}
// 3. 로그인
@RequestMapping(value = "/signin", method = RequestMethod.GET)
public String signin(Locale locale, Model model) {
return "signin";
}
// @RequestParam으로 받을 경우 한글이 깨진다. (영어는 괜찮음. 이 경우는 id, password 모두 영어만 있어서 괜찮지만 위 signupAction처럼 HttpServletRequest request로 통일시키자.
// @RequestMapping(value = "/signin_action", method = RequestMethod.POST)
// public String signinAction(Locale locale, Model model, HttpServletRequest request,
// @RequestParam("id") String id,
// @RequestParam("password") String password) {
// String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
// DataReader dataReader = new DataReader(dbUrl, "users");
// boolean result = false;
//
// dataReader.open(); // DataReader를 연다.
// try {
// MessageDigest digest = MessageDigest.getInstance("SHA-256");
// byte[] hash = digest.digest(password.getBytes("UTF-8"));
// StringBuffer hexString = new StringBuffer();
//
// // 해쉬한 문자열을 16진수(hex)로 바꿔주는 로직.
// for (int i = 0; i < hash.length; i++) {
// String hex = Integer.toHexString(0xff & hash[i]);
// if (hex.length() == 1) {
// hexString.append('0');
// }
// hexString.append(hex);
// }
//
// result = dataReader.signin(id, hexString.toString()); // DB에 SHA-256으로 해시한 암호가 저장되어 있으니 사용자가 입력한 암호 역시 SHA-256으로 해시해서 보내야 일치하는지 판별이 가능하다.
//
// } catch (Exception ex) {
// throw new RuntimeException(ex); // RuntimeException을 추가한다.
// }
// dataReader.close(); // DataReader를 닫는다.
//
// if (result) {
// HttpSession session = request.getSession();
// session.setAttribute("is_login", "true");
// return "success";
// } else {
// return "fail";
// }
// }
@RequestMapping(value = "/signin_action", method = RequestMethod.POST)
public String signinAction(Locale locale, Model model, HttpServletRequest request) throws UnsupportedEncodingException {
request.setCharacterEncoding("UTF-8"); // UTF-8 인코딩.
String id = request.getParameter("id");
String password = request.getParameter("password");
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
DataReader dataReader = new DataReader(dbUrl, "users");
boolean result = false;
dataReader.open(); // DataReader를 연다.
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(password.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();
// 해쉬한 문자열을 16진수(hex)로 바꿔주는 로직.
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
result = dataReader.signin(id, hexString.toString()); // DB에 SHA-256으로 해시한 암호가 저장되어 있으니 사용자가 입력한 암호 역시 SHA-256으로 해시해서 보내야 일치하는지 판별이 가능하다.
} catch (Exception ex) {
throw new RuntimeException(ex); // RuntimeException을 추가한다.
}
dataReader.close(); // DataReader를 닫는다.
if (result) {
HttpSession session = request.getSession();
session.setAttribute("is_login", "true");
return "success";
} else {
return "fail";
}
}
// 3.2 로그인 세션 확인 및 로그아웃
// 로그인 세션 확인 페이지로 이동.
@RequestMapping(value = "/private", method = RequestMethod.GET)
public String privatePage(Locale locale, Model model, HttpServletRequest request) {
HttpSession session = request.getSession();
String isLogin = (String)session.getAttribute("is_login");
if (isLogin != null && isLogin.equals("true")) {
return "private";
}
return "signin";
}
// 세션 로그아웃.
@RequestMapping(value = "/signout", method = RequestMethod.GET)
public String logout(Locale locale, Model model, HttpServletRequest request) {
HttpSession session = request.getSession();
session.invalidate(); // 세션을 무효화한다.
return "signin";
}
// 4. 정보수정
@RequestMapping(value = "/modify", method = RequestMethod.GET)
public String modify(Locale locale, Model model, @RequestParam("idx") String idx) {
model.addAttribute("idx", idx);
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
DataReader dataReader = new DataReader(dbUrl, "users");
dataReader.open();
try {
User sUser = dataReader.selectData(idx); // 자료형:User 클래스, 변수명:sUser, 값:DataReader가 SELECT한 idx 번호의 객체 row.
model.addAttribute("name", sUser.name);
model.addAttribute("id", sUser.id);
} catch (Exception e) {
// TODO: handle exception
}
dataReader.close();
return "modify";
}
@RequestMapping(value = "/modify_action", method = RequestMethod.POST)
public String modifyAction(Locale locale, Model model, HttpServletRequest request) throws UnsupportedEncodingException {
request.setCharacterEncoding("UTF-8"); // UTF-8 인코딩.
String idx = request.getParameter("idx");
String id = request.getParameter("id");
String name = request.getParameter("name");
String dbUrl = "//Users//꿈나무//SqliteDB//users.sqlite";
DataReader dataReader = new DataReader(dbUrl, "users");
dataReader.open(); // DataReader를 연다.
try {
dataReader.updateData(idx, name, id);
} catch (Exception ex) {
throw new RuntimeException(ex); // RuntimeException을 추가한다.
}
dataReader.close(); // DataReader를 닫는다.
return "modify";
}
}
DataReader.java
package com.KOPO.Users;
import java.sql.Connection;
import java.sql.DriverManager;
import org.sqlite.SQLiteConfig;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.sql.ResultSet;
public class DataReader {
private Connection connection;
private String dbfileName;
private String dbTableName;
static {
try {
Class.forName("org.sqlite.JDBC");
}catch (Exception e) {
e.printStackTrace();
}
}
public DataReader(String databaseFileName, String dbTableName) {
this.dbfileName = databaseFileName;
this.dbTableName = dbTableName;
}
public boolean open() {
try {
SQLiteConfig config = new SQLiteConfig();
this.connection = DriverManager.getConnection("jdbc:sqlite:/" +
this.dbfileName, config.toProperties());
}catch(SQLException e) {
e.printStackTrace();
return false;
}
return true;
}
public boolean close() {
if(this.connection == null) {
return true;
}
try {
this.connection.close();
}catch(SQLException e) {
e.printStackTrace();
return false;
}
return true;
}
// 1. 메인 홈
public String selectData() throws SQLException {
boolean result = false;
String query = "SELECT * FROM " + this.dbTableName + " WHERE ?;";
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
preparedStatement.setInt(1, 1);
ResultSet resultSet = preparedStatement.executeQuery();
String htmlText = "";
while (resultSet.next()) {
String idx = resultSet.getString("idx");
String name = resultSet.getString("name");
String id = resultSet.getString("id");
htmlText = htmlText
+ String.format("<tr>"
+ "<td>%s</td>"
+ "<td>%s</td>"
+ "<td>%s</td>"
+ "<td><a href='/Users/modify?idx=%s'>수정하기</a></td>",
idx, name, id, idx);
}
resultSet.close();
preparedStatement.close();
return htmlText;
}
// 2. 회원가입
// signupAction에 대한 Insert 쿼리를 날려주는 메소드를 생성한다.
// public int insertData(String name, String id, String password) throws SQLException {
// String query = "INSERT INTO " + this.dbTableName + " (name, id, password) VALUES('" + name + "', '" + id + "', '" + password + "');";
// Statement statement = this.connection.createStatement(); // Statement를 이용해 접속 엔진을 연다.
// int result = statement.executeUpdate(query); // 엔진을 이용해 쿼리를 보낸다.
// statement.close(); // 접속 엔진을 닫는다.
//
// return result;
// }
// signupAction을 Statement에서 PreparedStatement로 바꿔보자.
public int insertData(String name, String id, String password) throws SQLException {
String query = "INSERT INTO " + this.dbTableName + " (name, id, password) VALUES(?, ?, ?);";
PreparedStatement preparedStatement = this.connection.prepareStatement(query); // PreparedStatement를 이용해 접속 엔진을 연다.
preparedStatement.setString(1, name); // 1번째 물음표에 name을 넣는다.
preparedStatement.setString(2, id); // 2번째 물음표에 id를 넣는다.
preparedStatement.setString(3, password);
int result = preparedStatement.executeUpdate(); // 엔진을 이용해 쿼리를 보낸다.
preparedStatement.close(); // 접속 엔진을 닫는다.
return result;
}
// 3. 로그인
// signinAction에 대한 Select 쿼리를 날려주는 메소드를 생성한다.
public boolean signin(String id, String password) throws SQLException {
boolean result = false;
String query = "SELECT * FROM " + this.dbTableName + " WHERE id=? AND password=?;";
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
preparedStatement.setString(1, id);
preparedStatement.setString(2, password);
ResultSet resultSet = preparedStatement.executeQuery(); // ResultSet은 쿼리 결과를 그대로 받아온다.
if (resultSet.next()) {
result = true; // ResultSet을 이용해 일치하면 return값인 result에 boolean 형태의 값을 넣너준다.
}
resultSet.close(); // 반드시 닫아준다!
preparedStatement.close(); // 반드시 닫아준다!
return result;
}
// 4. 정보수정 (객체를 return하는 selectData를 만든다. 위 selectData는 인풋 파라미터가 없었다. 다형성을 이용해 구분한다.)
public User selectData(String idx) throws SQLException {
boolean result = false;
String query = "SELECT * FROM " + this.dbTableName + " WHERE idx=?;";
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
preparedStatement.setString(1, idx);
ResultSet resultSet = preparedStatement.executeQuery();
User selectedUser = new User();
if (resultSet.next()) {
selectedUser.idx = resultSet.getInt("idx");
selectedUser.name = resultSet.getString("name");
selectedUser.id = resultSet.getString("id");
}
resultSet.close();
preparedStatement.close();
return selectedUser;
}
public void updateData(String idx, String name, String id) throws SQLException {
String query = "UPDATE " + this.dbTableName + " SET name=?, id=? WHERE idx=?;";
PreparedStatement preparedStatement = this.connection.prepareStatement(query);
preparedStatement.setString(1, name);
preparedStatement.setString(2, id);
preparedStatement.setString(3, idx);
int result = preparedStatement.executeUpdate();
preparedStatement.close();
}
// public void updateData(String idx, String name, String id) throws SQLException {
// String query = "UPDATE " + this.dbTableName + " SET name='" + name + "', id='" + id + "' WHERE idx=" + idx + ";";
// Statement statement = this.connection.createStatement();
// int result = statement.executeUpdate(query);
// statement.close();
// }
}
User.java
package com.KOPO.Users;
public class User {
int idx;
String name;
String id;
String password;
}
home.css
@charset "UTF-8";
table {
border-collapse: collapse;
width: 90%;
margin: 0 auto;
}
td, th {
padding: 5px;
text-align: center;
}
th {
background: #A4A4A4;
color: #fff;
}
td a {
background: #84FA7A;
padding: 5px 20px;
text-decoration: none;
border-radius: 5px;
}
sign_up.css
form {
width: 500px;
padding: 40px;
background: #eee;
border-radius: 10px;
margin: 0 auto;
}
form input[type="text"], form input[type="password"] {
width: 100%;
margin: 10px 0px;
padding: 10px;
}
form input[type="submit"] {
display: block;
padding: 10px 100px;
margin: 0 auto;
}
home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/home.css">
</head>
<body>
<h1>
User Data
</h1>
<a href="/Users/private">개인정보</a><br /> <!-- /Users/ 는 생략 가능하다. private만 써도 된다. -->
<a href="/Users/signin">로그인</a> <a href="/Users/signout">로그아웃</a> <a href="/Users/signup">회원가입</a><br/><br/>
<table>
<thead>
<tr>
<th>idx</th><th>이름</th><th>아이디</th><th></th>
</tr>
</thead>
<tbody>
${userInfo }
</tbody>
</table>
</body>
</html>
signup.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/sign_up.css">
<title>회원가입</title>
</head>
<body>
<h1>회원가입</h1>
<a href="/Users/">홈으로</a><br /><br />
<!-- form 태그는 type="submit" 버튼을 누르면 action을 실행한다. name은 각각 파라미터 이름이 되고, 입력된 값은 각 파라미터의 값이 된다. HomeController에서 HttpServletRequest가 .getParameter를 통해 파라미터와 값에 접근한다. -->
<form action="/Users/signup_action" method = "post">
이름 <input type="text" name="name" placeholder="이름을 입력해주세요" />
<br />
ID <input type="text" name="id" placeholder="아이디를 입력해주세요" />
<br />
PW <input type="password" name="password" placeholder="패스워드를 입력해주세요" />
<br /><br />
<input type="submit" name="" value="회원가입" />
</form>
</body>
</html>
signin.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/sign_up.css">
<title>로그인</title>
</head>
<body>
<h3>로그인</h3>
<a href="/Users/">홈으로</a><br /><br />
<form action="/Users/signin_action" method="post">
ID <input type="text" name="id" placeholder="아이디를 입력해주세요" />
<br />
PW <input type="text" name="password" placeholder="패스워드를 입력해주세요" />
<br />
<input type="submit" value="로그인" />
</form>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>signin success</title>
</head>
<body>
<p>로그인 되었습니다.</p>
<a href="/Users/">홈으로</a>
</body>
</html>
fail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>signin fail</title>
</head>
<body>
<p>로그인에 실패하였습니다. 아이디와 비밀번호를 확인하세요.</p>
<a href="/Users/">홈으로</a> <a href="/Users/signin">로그인 페이지로 돌아가기</a>
</body>
</html>
private.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>비밀 페이지 입니다.</h3>
<a href="/Users/">홈으로</a>
</body>
</html>
modify.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/sign_up.css">
<title>정보수정</title>
</head>
<body>
<h1>정보수정</h1>
<a href="/Users/">홈으로</a><br /><br />
<!-- form 태그는 type="submit" 버튼을 누르면 action을 실행한다. name은 각각 파라미터 이름이 되고, 입력된 값은 각 파라미터의 값이 된다. HomeController에서 HttpServletRequest가 .getParameter를 통해 파라미터와 값에 접근한다. -->
<form action="/Users/modify_action" method = "post">
<input type="hidden" name="idx" value="${idx }" /> <!-- value에 DB에 있는 현재 정보를 불러와 띄워준다. -->
이름 <input type="text" name="name" placeholder="이름을 입력해주세요" value="${name }" />
<br />
ID <input type="text" name="id" placeholder="아이디를 입력해주세요" value="${id }" />
<!-- <br />
<input type="password" name="password" placeholder="패스워드를 입력해주세요" /> -->
<br /><br />
<input type="submit" name="" value="수정하기" />
</form>
</body>
</html>
'개발자 > Java' 카테고리의 다른 글
전자정부 프레임 시작하기 1 (설치) (0) | 2020.07.08 |
---|---|
리눅스에서 자바 설치 openjdk 내려받기 (0) | 2020.06.30 |
Java (자바) Read-only file system 오류 (0) | 2020.06.07 |
Eclipse (이클립스) 서버 실행 불가 (0) | 2020.06.07 |
Java (자바) HomeController의 locale, model, model.addAttribute() (0) | 2020.06.05 |